Skip to content

Commit 9c31d8d

Browse files
committed
collector: Add ifalias label to ethtool collector
Signed-off-by: Justinas Trunce <justinas@trunce.net> Signed-off-by: jtrunce <justinas.trunce@hostinger.com>
1 parent 04d9906 commit 9c31d8d

3 files changed

Lines changed: 184 additions & 40 deletions

File tree

collector/ethtool_linux.go

Lines changed: 64 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ var (
4141
ethtoolDeviceInclude = kingpin.Flag("collector.ethtool.device-include", "Regexp of ethtool devices to include (mutually exclusive to device-exclude).").String()
4242
ethtoolDeviceExclude = kingpin.Flag("collector.ethtool.device-exclude", "Regexp of ethtool devices to exclude (mutually exclusive to device-include).").String()
4343
ethtoolIncludedMetrics = kingpin.Flag("collector.ethtool.metrics-include", "Regexp of ethtool stats to include.").Default(".*").String()
44+
ethtoolAddIfAliasLabel = kingpin.Flag("collector.ethtool.label-ifalias", "Add ifalias label").Default("false").Bool()
4445
ethtoolReceivedRegex = regexp.MustCompile(`(^|_)rx(_|$)`)
4546
ethtoolTransmitRegex = regexp.MustCompile(`(^|_)tx(_|$)`)
4647
)
@@ -78,6 +79,7 @@ type ethtoolCollector struct {
7879
infoDesc *prometheus.Desc
7980
metricsPattern *regexp.Regexp
8081
logger *slog.Logger
82+
ifAliasEnabled bool
8183
}
8284

8385
// makeEthtoolCollector is the internal constructor for EthtoolCollector.
@@ -94,6 +96,9 @@ func makeEthtoolCollector(logger *slog.Logger) (*ethtoolCollector, error) {
9496
return nil, fmt.Errorf("failed to initialize ethtool library: %w", err)
9597
}
9698

99+
deviceLabels := []string{"device"}
100+
infoDescHelp := "A metric with a constant '1' value labeled by bus_info, device, driver, expansion_rom_version, firmware_version, version."
101+
97102
if *ethtoolDeviceInclude != "" {
98103
logger.Info("Parsed flag --collector.ethtool.device-include", "flag", *ethtoolDeviceInclude)
99104
}
@@ -103,6 +108,11 @@ func makeEthtoolCollector(logger *slog.Logger) (*ethtoolCollector, error) {
103108
if *ethtoolIncludedMetrics != "" {
104109
logger.Info("Parsed flag --collector.ethtool.metrics-include", "flag", *ethtoolIncludedMetrics)
105110
}
111+
if *ethtoolAddIfAliasLabel {
112+
logger.Info("Parsed flag --collector.ethtool.label-ifalias", "flag", *ethtoolAddIfAliasLabel)
113+
deviceLabels = []string{"device", "ifalias"}
114+
infoDescHelp = "A metric with a constant '1' value labeled by bus_info, device, ifalias, driver, expansion_rom_version, firmware_version, version."
115+
}
106116

107117
// Pre-populate some common ethtool metrics.
108118
return &ethtoolCollector{
@@ -115,96 +125,96 @@ func makeEthtoolCollector(logger *slog.Logger) (*ethtoolCollector, error) {
115125
"rx_bytes": prometheus.NewDesc(
116126
prometheus.BuildFQName(namespace, "ethtool", "received_bytes_total"),
117127
"Network interface bytes received",
118-
[]string{"device"}, nil,
128+
deviceLabels, nil,
119129
),
120130
"rx_dropped": prometheus.NewDesc(
121131
prometheus.BuildFQName(namespace, "ethtool", "received_dropped_total"),
122132
"Number of received frames dropped",
123-
[]string{"device"}, nil,
133+
deviceLabels, nil,
124134
),
125135
"rx_errors": prometheus.NewDesc(
126136
prometheus.BuildFQName(namespace, "ethtool", "received_errors_total"),
127137
"Number of received frames with errors",
128-
[]string{"device"}, nil,
138+
deviceLabels, nil,
129139
),
130140
"rx_packets": prometheus.NewDesc(
131141
prometheus.BuildFQName(namespace, "ethtool", "received_packets_total"),
132142
"Network interface packets received",
133-
[]string{"device"}, nil,
143+
deviceLabels, nil,
134144
),
135145
"tx_bytes": prometheus.NewDesc(
136146
prometheus.BuildFQName(namespace, "ethtool", "transmitted_bytes_total"),
137147
"Network interface bytes sent",
138-
[]string{"device"}, nil,
148+
deviceLabels, nil,
139149
),
140150
"tx_errors": prometheus.NewDesc(
141151
prometheus.BuildFQName(namespace, "ethtool", "transmitted_errors_total"),
142152
"Number of sent frames with errors",
143-
[]string{"device"}, nil,
153+
deviceLabels, nil,
144154
),
145155
"tx_packets": prometheus.NewDesc(
146156
prometheus.BuildFQName(namespace, "ethtool", "transmitted_packets_total"),
147157
"Network interface packets sent",
148-
[]string{"device"}, nil,
158+
deviceLabels, nil,
149159
),
150160

151161
// link info
152162
"supported_port": prometheus.NewDesc(
153163
prometheus.BuildFQName(namespace, "network", "supported_port_info"),
154164
"Type of ports or PHYs supported by network device",
155-
[]string{"device", "type"}, nil,
165+
append(deviceLabels, "type"), nil,
156166
),
157167
"supported_speed": prometheus.NewDesc(
158168
prometheus.BuildFQName(namespace, "network", "supported_speed_bytes"),
159169
"Combination of speeds and features supported by network device",
160-
[]string{"device", "duplex", "mode"}, nil,
170+
append(deviceLabels, "duplex", "mode"), nil,
161171
),
162172
"supported_autonegotiate": prometheus.NewDesc(
163173
prometheus.BuildFQName(namespace, "network", "autonegotiate_supported"),
164174
"If this port device supports autonegotiate",
165-
[]string{"device"}, nil,
175+
deviceLabels, nil,
166176
),
167177
"supported_pause": prometheus.NewDesc(
168178
prometheus.BuildFQName(namespace, "network", "pause_supported"),
169179
"If this port device supports pause frames",
170-
[]string{"device"}, nil,
180+
deviceLabels, nil,
171181
),
172182
"supported_asymmetricpause": prometheus.NewDesc(
173183
prometheus.BuildFQName(namespace, "network", "asymmetricpause_supported"),
174184
"If this port device supports asymmetric pause frames",
175-
[]string{"device"}, nil,
185+
deviceLabels, nil,
176186
),
177187
"advertised_speed": prometheus.NewDesc(
178188
prometheus.BuildFQName(namespace, "network", "advertised_speed_bytes"),
179189
"Combination of speeds and features offered by network device",
180-
[]string{"device", "duplex", "mode"}, nil,
190+
append(deviceLabels, "duplex", "mode"), nil,
181191
),
182192
"advertised_autonegotiate": prometheus.NewDesc(
183193
prometheus.BuildFQName(namespace, "network", "autonegotiate_advertised"),
184194
"If this port device offers autonegotiate",
185-
[]string{"device"}, nil,
195+
deviceLabels, nil,
186196
),
187197
"advertised_pause": prometheus.NewDesc(
188198
prometheus.BuildFQName(namespace, "network", "pause_advertised"),
189199
"If this port device offers pause capability",
190-
[]string{"device"}, nil,
200+
deviceLabels, nil,
191201
),
192202
"advertised_asymmetricpause": prometheus.NewDesc(
193203
prometheus.BuildFQName(namespace, "network", "asymmetricpause_advertised"),
194204
"If this port device offers asymmetric pause capability",
195-
[]string{"device"}, nil,
205+
deviceLabels, nil,
196206
),
197207
"autonegotiate": prometheus.NewDesc(
198208
prometheus.BuildFQName(namespace, "network", "autonegotiate"),
199209
"If this port is using autonegotiate",
200-
[]string{"device"}, nil,
210+
deviceLabels, nil,
201211
),
202212
},
203213
infoDesc: prometheus.NewDesc(
204-
prometheus.BuildFQName(namespace, "ethtool", "info"),
205-
"A metric with a constant '1' value labeled by bus_info, device, driver, expansion_rom_version, firmware_version, version.",
206-
[]string{"bus_info", "device", "driver", "expansion_rom_version", "firmware_version", "version"}, nil,
214+
prometheus.BuildFQName(namespace, "ethtool", "info"), infoDescHelp,
215+
append(append([]string{"bus_info"}, deviceLabels...), "driver", "expansion_rom_version", "firmware_version", "version"), nil,
207216
),
217+
ifAliasEnabled: *ethtoolAddIfAliasLabel,
208218
}, nil
209219
}
210220

@@ -225,10 +235,24 @@ func NewEthtoolCollector(logger *slog.Logger) (Collector, error) {
225235
return makeEthtoolCollector(logger)
226236
}
227237

238+
func (c *ethtoolCollector) deviceLabelValues(device, ifAlias string) []string {
239+
if c.ifAliasEnabled {
240+
return []string{device, ifAlias}
241+
}
242+
return []string{device}
243+
}
244+
245+
func (c *ethtoolCollector) deviceLabelNames() []string {
246+
if c.ifAliasEnabled {
247+
return []string{"device", "ifalias"}
248+
}
249+
return []string{"device"}
250+
}
251+
228252
// updatePortCapabilities generates metrics for autonegotiate, pause and asymmetricpause.
229253
// The bit offsets here correspond to ethtool_link_mode_bit_indices in linux/include/uapi/linux/ethtool.h
230254
// https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/ethtool.h
231-
func (c *ethtoolCollector) updatePortCapabilities(ch chan<- prometheus.Metric, prefix string, device string, linkModes uint32) {
255+
func (c *ethtoolCollector) updatePortCapabilities(ch chan<- prometheus.Metric, prefix string, device string, ifAlias string, linkModes uint32) {
232256
var (
233257
autonegotiate = 0.0
234258
pause = 0.0
@@ -243,15 +267,15 @@ func (c *ethtoolCollector) updatePortCapabilities(ch chan<- prometheus.Metric, p
243267
if linkModes&(1<<unix.ETHTOOL_LINK_MODE_Asym_Pause_BIT) != 0 {
244268
asymmetricPause = 1.0
245269
}
246-
ch <- prometheus.MustNewConstMetric(c.entry(fmt.Sprintf("%s_autonegotiate", prefix)), prometheus.GaugeValue, autonegotiate, device)
247-
ch <- prometheus.MustNewConstMetric(c.entry(fmt.Sprintf("%s_pause", prefix)), prometheus.GaugeValue, pause, device)
248-
ch <- prometheus.MustNewConstMetric(c.entry(fmt.Sprintf("%s_asymmetricpause", prefix)), prometheus.GaugeValue, asymmetricPause, device)
270+
ch <- prometheus.MustNewConstMetric(c.entry(fmt.Sprintf("%s_autonegotiate", prefix)), prometheus.GaugeValue, autonegotiate, c.deviceLabelValues(device, ifAlias)...)
271+
ch <- prometheus.MustNewConstMetric(c.entry(fmt.Sprintf("%s_pause", prefix)), prometheus.GaugeValue, pause, c.deviceLabelValues(device, ifAlias)...)
272+
ch <- prometheus.MustNewConstMetric(c.entry(fmt.Sprintf("%s_asymmetricpause", prefix)), prometheus.GaugeValue, asymmetricPause, c.deviceLabelValues(device, ifAlias)...)
249273
}
250274

251275
// updatePortInfo generates port type metrics to indicate if the network devices supports Twisted Pair, optical fiber, etc.
252276
// The bit offsets here correspond to ethtool_link_mode_bit_indices in linux/include/uapi/linux/ethtool.h
253277
// https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/ethtool.h
254-
func (c *ethtoolCollector) updatePortInfo(ch chan<- prometheus.Metric, device string, linkModes uint32) {
278+
func (c *ethtoolCollector) updatePortInfo(ch chan<- prometheus.Metric, device string, ifAlias string, linkModes uint32) {
255279
for name, bit := range map[string]int{
256280
"TP": unix.ETHTOOL_LINK_MODE_TP_BIT,
257281
"AUI": unix.ETHTOOL_LINK_MODE_AUI_BIT,
@@ -261,7 +285,7 @@ func (c *ethtoolCollector) updatePortInfo(ch chan<- prometheus.Metric, device st
261285
"Backplane": unix.ETHTOOL_LINK_MODE_Backplane_BIT,
262286
} {
263287
if linkModes&(1<<bit) != 0 {
264-
ch <- prometheus.MustNewConstMetric(c.entry("supported_port"), prometheus.GaugeValue, 1.0, device, name)
288+
ch <- prometheus.MustNewConstMetric(c.entry("supported_port"), prometheus.GaugeValue, 1.0, append(c.deviceLabelValues(device, ifAlias), name)...)
265289
}
266290

267291
}
@@ -270,7 +294,7 @@ func (c *ethtoolCollector) updatePortInfo(ch chan<- prometheus.Metric, device st
270294
// updateSpeeds generates metrics corresponding to the speeds and duplex modes supported or advertised by the network device.
271295
// The bit offsets here correspond to ethtool_link_mode_bit_indices in linux/include/uapi/linux/ethtool.h
272296
// https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/ethtool.h
273-
func (c *ethtoolCollector) updateSpeeds(ch chan<- prometheus.Metric, prefix string, device string, linkModes uint32) {
297+
func (c *ethtoolCollector) updateSpeeds(ch chan<- prometheus.Metric, prefix string, device string, ifAlias string, linkModes uint32) {
274298
linkMode := fmt.Sprintf("%s_speed", prefix)
275299
const (
276300
full = "full"
@@ -365,13 +389,13 @@ func (c *ethtoolCollector) updateSpeeds(ch chan<- prometheus.Metric, prefix stri
365389
} {
366390
if linkModes&(1<<bit) != 0 {
367391
ch <- prometheus.MustNewConstMetric(c.entry(linkMode), prometheus.GaugeValue,
368-
float64(labels.speed)*Mbps, device, labels.duplex, fmt.Sprintf("%dbase%s", labels.speed, labels.phy))
392+
float64(labels.speed)*Mbps, append(c.deviceLabelValues(device, ifAlias), labels.duplex, fmt.Sprintf("%dbase%s", labels.speed, labels.phy))...)
369393
}
370394
}
371395
}
372396

373397
func (c *ethtoolCollector) Update(ch chan<- prometheus.Metric) error {
374-
netClass, err := c.fs.NetClassDevices()
398+
netClass, err := c.fs.NetClass()
375399
if err != nil {
376400
if errors.Is(err, os.ErrNotExist) || errors.Is(err, os.ErrPermission) {
377401
c.logger.Debug("Could not read netclass file", "err", err)
@@ -384,7 +408,7 @@ func (c *ethtoolCollector) Update(ch chan<- prometheus.Metric) error {
384408
return fmt.Errorf("no network devices found")
385409
}
386410

387-
for _, device := range netClass {
411+
for device, params := range netClass {
388412
var stats map[string]uint64
389413
var err error
390414

@@ -394,12 +418,13 @@ func (c *ethtoolCollector) Update(ch chan<- prometheus.Metric) error {
394418

395419
linkInfo, err := c.ethtool.LinkInfo(device)
396420
if err == nil {
397-
c.updateSpeeds(ch, "supported", device, linkInfo.Supported)
398-
c.updatePortInfo(ch, device, linkInfo.Supported)
399-
c.updatePortCapabilities(ch, "supported", device, linkInfo.Supported)
400-
c.updateSpeeds(ch, "advertised", device, linkInfo.Advertising)
401-
c.updatePortCapabilities(ch, "advertised", device, linkInfo.Advertising)
402-
ch <- prometheus.MustNewConstMetric(c.entry("autonegotiate"), prometheus.GaugeValue, float64(linkInfo.Autoneg), device)
421+
c.updateSpeeds(ch, "supported", device, params.IfAlias, linkInfo.Supported)
422+
c.updatePortInfo(ch, device, params.IfAlias, linkInfo.Supported)
423+
c.updatePortCapabilities(ch, "supported", device, params.IfAlias, linkInfo.Supported)
424+
c.updateSpeeds(ch, "advertised", device, params.IfAlias, linkInfo.Advertising)
425+
c.updatePortCapabilities(ch, "advertised", device, params.IfAlias, linkInfo.Advertising)
426+
ch <- prometheus.MustNewConstMetric(c.entry("autonegotiate"), prometheus.GaugeValue, float64(linkInfo.Autoneg),
427+
c.deviceLabelValues(device, params.IfAlias)...)
403428
} else {
404429
if errno, ok := err.(syscall.Errno); ok {
405430
if err == unix.EOPNOTSUPP {
@@ -416,7 +441,7 @@ func (c *ethtoolCollector) Update(ch chan<- prometheus.Metric) error {
416441

417442
if err == nil {
418443
ch <- prometheus.MustNewConstMetric(c.infoDesc, prometheus.GaugeValue, 1.0,
419-
drvInfo.BusInfo, device, drvInfo.Driver, drvInfo.EromVersion, drvInfo.FwVersion, drvInfo.Version)
444+
append(append([]string{drvInfo.BusInfo}, c.deviceLabelValues(device, params.IfAlias)...), drvInfo.Driver, drvInfo.EromVersion, drvInfo.FwVersion, drvInfo.Version)...)
420445
} else {
421446
if errno, ok := err.(syscall.Errno); ok {
422447
if err == unix.EOPNOTSUPP {
@@ -491,7 +516,7 @@ func (c *ethtoolCollector) Update(ch chan<- prometheus.Metric) error {
491516
// Check to see if this metric exists; if not then create it and store it in c.entries.
492517
entry := c.entryWithCreate(metric, metricFQName)
493518
ch <- prometheus.MustNewConstMetric(
494-
entry, prometheus.UntypedValue, float64(val), device)
519+
entry, prometheus.UntypedValue, float64(val), c.deviceLabelValues(device, params.IfAlias)...)
495520
}
496521
}
497522

@@ -506,7 +531,7 @@ func (c *ethtoolCollector) entryWithCreate(key, metricFQName string) *prometheus
506531
c.entries[key] = prometheus.NewDesc(
507532
metricFQName,
508533
fmt.Sprintf("Network interface %s", key),
509-
[]string{"device"}, nil,
534+
c.deviceLabelNames(), nil,
510535
)
511536
}
512537

0 commit comments

Comments
 (0)