Add optional interface alias in netstat (#130)

* Check creation of CCMessage in NATS receiver

* add optional interface aliases for netstatMetric

* small fix

---------

Co-authored-by: Thomas Roehl <thomas.roehl@fau.de>
Co-authored-by: exterr2f <Robert.Externbrink@rub.de>
Co-authored-by: Thomas Gruber <Thomas.Roehl@googlemail.com>
This commit is contained in:
brinkcoder 2025-02-19 11:32:15 +01:00 committed by GitHub
parent 6901b06e44
commit 0f92f10b66
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 49 additions and 23 deletions

View File

@ -10,15 +10,16 @@ import (
"time" "time"
cclog "github.com/ClusterCockpit/cc-metric-collector/pkg/ccLogger" cclog "github.com/ClusterCockpit/cc-metric-collector/pkg/ccLogger"
lp "github.com/ClusterCockpit/cc-energy-manager/pkg/cc-message" lp "github.com/ClusterCockpit/cc-lib/ccMessage"
) )
const NETSTATFILE = "/proc/net/dev" const NETSTATFILE = "/proc/net/dev"
type NetstatCollectorConfig struct { type NetstatCollectorConfig struct {
IncludeDevices []string `json:"include_devices"` IncludeDevices []string `json:"include_devices"`
SendAbsoluteValues bool `json:"send_abs_values"` SendAbsoluteValues bool `json:"send_abs_values"`
SendDerivedValues bool `json:"send_derived_values"` SendDerivedValues bool `json:"send_derived_values"`
InterfaceAliases map[string][]string `json:"interface_aliases,omitempty"`
} }
type NetstatCollectorMetric struct { type NetstatCollectorMetric struct {
@ -32,9 +33,26 @@ type NetstatCollectorMetric struct {
type NetstatCollector struct { type NetstatCollector struct {
metricCollector metricCollector
config NetstatCollectorConfig config NetstatCollectorConfig
matches map[string][]NetstatCollectorMetric aliasToCanonical map[string]string
lastTimestamp time.Time matches map[string][]NetstatCollectorMetric
lastTimestamp time.Time
}
func (m *NetstatCollector) buildAliasMapping() {
m.aliasToCanonical = make(map[string]string)
for canon, aliases := range m.config.InterfaceAliases {
for _, alias := range aliases {
m.aliasToCanonical[alias] = canon
}
}
}
func getCanonicalName(raw string, aliasToCanonical map[string]string) string {
if canon, ok := aliasToCanonical[raw]; ok {
return canon
}
return raw
} }
func (m *NetstatCollector) Init(config json.RawMessage) error { func (m *NetstatCollector) Init(config json.RawMessage) error {
@ -77,6 +95,8 @@ func (m *NetstatCollector) Init(config json.RawMessage) error {
} }
} }
m.buildAliasMapping()
// Check access to net statistic file // Check access to net statistic file
file, err := os.Open(NETSTATFILE) file, err := os.Open(NETSTATFILE)
if err != nil { if err != nil {
@ -97,18 +117,20 @@ func (m *NetstatCollector) Init(config json.RawMessage) error {
// Split line into fields // Split line into fields
f := strings.Fields(l) f := strings.Fields(l)
// Get net device entry // Get raw and canonical names
dev := strings.Trim(f[0], ": ") raw := strings.Trim(f[0], ": ")
canonical := getCanonicalName(raw, m.aliasToCanonical)
// Check if device is a included device // Check if device is a included device
if _, ok := stringArrayContains(m.config.IncludeDevices, dev); ok { if _, ok := stringArrayContains(m.config.IncludeDevices, canonical); ok {
tags := map[string]string{"stype": "network", "stype-id": dev, "type": "node"} // Tag will contain original device name (raw).
tags := map[string]string{"stype": "network", "stype-id": raw, "type": "node"}
meta_unit_byte := map[string]string{"source": m.name, "group": "Network", "unit": "bytes"} meta_unit_byte := map[string]string{"source": m.name, "group": "Network", "unit": "bytes"}
meta_unit_byte_per_sec := map[string]string{"source": m.name, "group": "Network", "unit": "bytes/sec"} meta_unit_byte_per_sec := map[string]string{"source": m.name, "group": "Network", "unit": "bytes/sec"}
meta_unit_pkts := map[string]string{"source": m.name, "group": "Network", "unit": "packets"} meta_unit_pkts := map[string]string{"source": m.name, "group": "Network", "unit": "packets"}
meta_unit_pkts_per_sec := map[string]string{"source": m.name, "group": "Network", "unit": "packets/sec"} meta_unit_pkts_per_sec := map[string]string{"source": m.name, "group": "Network", "unit": "packets/sec"}
m.matches[dev] = []NetstatCollectorMetric{ m.matches[canonical] = []NetstatCollectorMetric{
{ {
name: "net_bytes_in", name: "net_bytes_in",
index: fieldReceiveBytes, index: fieldReceiveBytes,
@ -143,7 +165,6 @@ func (m *NetstatCollector) Init(config json.RawMessage) error {
}, },
} }
} }
} }
if len(m.matches) == 0 { if len(m.matches) == 0 {
@ -164,7 +185,7 @@ func (m *NetstatCollector) Read(interval time.Duration, output chan lp.CCMessage
// Save current timestamp // Save current timestamp
m.lastTimestamp = now m.lastTimestamp = now
file, err := os.Open(string(NETSTATFILE)) file, err := os.Open(NETSTATFILE)
if err != nil { if err != nil {
cclog.ComponentError(m.name, err.Error()) cclog.ComponentError(m.name, err.Error())
return return
@ -183,11 +204,12 @@ func (m *NetstatCollector) Read(interval time.Duration, output chan lp.CCMessage
// Split line into fields // Split line into fields
f := strings.Fields(l) f := strings.Fields(l)
// Get net device entry // Get raw and canonical names
dev := strings.Trim(f[0], ":") raw := strings.Trim(f[0], ":")
canonical := getCanonicalName(raw, m.aliasToCanonical)
// Check if device is a included device // Check if device is a included device
if devmetrics, ok := m.matches[dev]; ok { if devmetrics, ok := m.matches[canonical]; ok {
for i := range devmetrics { for i := range devmetrics {
metric := &devmetrics[i] metric := &devmetrics[i]

View File

@ -4,14 +4,19 @@
```json ```json
"netstat": { "netstat": {
"include_devices": [ "include_devices": [
"eth0" "eth0",
"eno1"
], ],
"send_abs_values" : true, "send_abs_values": true,
"send_derived_values" : true "send_derived_values": true,
"interface_aliases": {
"eno1": ["eno1np0", "eno1_alt"],
"eth0": ["eth0_alias"]
}
} }
``` ```
The `netstat` collector reads data from `/proc/net/dev` and outputs a handful **node** metrics. With the `include_devices` list you can specify which network devices should be measured. **Note**: Most other collectors use an _exclude_ list instead of an include list. The `netstat` collector reads data from `/proc/net/dev` and outputs a handful **node** metrics. With the `include_devices` list you can specify which network devices should be measured. **Note**: Most other collectors use an _exclude_ list instead of an include list. Optionally, you can define an interface_aliases mapping. For each canonical device (as listed in include_devices), you may provide an array of aliases that may be reported by the system. When an alias is detected, it is preferred for matching, while the output tag stype-id always shows the actual system-reported name.
Metrics: Metrics:
* `net_bytes_in` (`unit=bytes`) * `net_bytes_in` (`unit=bytes`)
@ -23,5 +28,4 @@ Metrics:
* `net_pkts_in_bw` (`unit=packets/sec` if `send_derived_values == true`) * `net_pkts_in_bw` (`unit=packets/sec` if `send_derived_values == true`)
* `net_pkts_out_bw` (`unit=packets/sec` if `send_derived_values == true`) * `net_pkts_out_bw` (`unit=packets/sec` if `send_derived_values == true`)
The device name is added as tag `stype=network,stype-id=<device>`. The device name is added as tag `stype=network,stype-id=<device>`.