pull/3514/merge
Maximilian Wilhelm 2 days ago committed by GitHub
commit 12f72b657d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 49
      collector/ethtool_linux.go
  2. 87
      collector/ethtool_linux_test.go
  3. 13
      collector/fixtures/ethtool/eth0/channels

@ -49,6 +49,7 @@ type Ethtool interface {
DriverInfo(string) (ethtool.DrvInfo, error)
Stats(string) (map[string]uint64, error)
LinkInfo(string) (ethtool.EthtoolCmd, error)
GetChannels(string) (ethtool.Channels, error)
}
type ethtoolLibrary struct {
@ -69,6 +70,10 @@ func (e *ethtoolLibrary) LinkInfo(intf string) (ethtool.EthtoolCmd, error) {
return ethtoolCmd, err
}
func (e *ethtoolLibrary) GetChannels(intf string) (ethtool.Channels, error) {
return e.ethtool.GetChannels(intf)
}
type ethtoolCollector struct {
fs sysfs.FS
entries map[string]*prometheus.Desc
@ -199,6 +204,21 @@ func makeEthtoolCollector(logger *slog.Logger) (*ethtoolCollector, error) {
"If this port is using autonegotiate",
[]string{"device"}, nil,
),
// channel info
//
// Each metric will have four instances differentiated by its type,
// with type being one of rx, tx, other, or combined.
"channels_max": prometheus.NewDesc(
prometheus.BuildFQName(namespace, "ethtool", "channels_max"),
"Maximum supported network interface channels",
[]string{"device", "type"}, nil,
),
"channels_current": prometheus.NewDesc(
prometheus.BuildFQName(namespace, "ethtool", "channels_current"),
"Currently configured network interface channels",
[]string{"device", "type"}, nil,
),
},
infoDesc: prometheus.NewDesc(
prometheus.BuildFQName(namespace, "ethtool", "info"),
@ -370,6 +390,33 @@ func (c *ethtoolCollector) updateSpeeds(ch chan<- prometheus.Metric, prefix stri
}
}
func (c *ethtoolCollector) updateChannels(ch chan<- prometheus.Metric, device string) {
channels, err := c.ethtool.GetChannels(device)
if err != nil {
if errno, ok := err.(syscall.Errno); ok {
if err == unix.EOPNOTSUPP {
c.logger.Debug("ethtool driver info error", "err", err, "device", device, "errno", uint(errno))
} else if errno != 0 {
c.logger.Error("ethtool get channels error", "err", err, "device", device, "errno", uint(errno))
}
} else {
c.logger.Error("ethtool get channels error", "err", err, "device", device)
}
return
}
ch <- prometheus.MustNewConstMetric(c.entry("channels_max"), prometheus.GaugeValue, float64(channels.MaxRx), device, "rx")
ch <- prometheus.MustNewConstMetric(c.entry("channels_max"), prometheus.GaugeValue, float64(channels.MaxTx), device, "tx")
ch <- prometheus.MustNewConstMetric(c.entry("channels_max"), prometheus.GaugeValue, float64(channels.MaxOther), device, "other")
ch <- prometheus.MustNewConstMetric(c.entry("channels_max"), prometheus.GaugeValue, float64(channels.MaxCombined), device, "combined")
ch <- prometheus.MustNewConstMetric(c.entry("channels_current"), prometheus.GaugeValue, float64(channels.RxCount), device, "rx")
ch <- prometheus.MustNewConstMetric(c.entry("channels_current"), prometheus.GaugeValue, float64(channels.TxCount), device, "tx")
ch <- prometheus.MustNewConstMetric(c.entry("channels_current"), prometheus.GaugeValue, float64(channels.OtherCount), device, "other")
ch <- prometheus.MustNewConstMetric(c.entry("channels_current"), prometheus.GaugeValue, float64(channels.CombinedCount), device, "combined")
}
func (c *ethtoolCollector) Update(ch chan<- prometheus.Metric) error {
netClass, err := c.fs.NetClassDevices()
if err != nil {
@ -429,6 +476,8 @@ func (c *ethtoolCollector) Update(ch chan<- prometheus.Metric) error {
}
}
c.updateChannels(ch, device)
stats, err = c.ethtool.Stats(device)
// If Stats() returns EOPNOTSUPP it doesn't support ethtool stats. Log that only at Debug level.

@ -257,6 +257,81 @@ func (e *EthtoolFixture) LinkInfo(intf string) (ethtool.EthtoolCmd, error) {
return res, err
}
func (e *EthtoolFixture) GetChannels(intf string) (ethtool.Channels, error) {
res := ethtool.Channels{}
fixtureFile, err := os.Open(filepath.Join(e.fixturePath, intf, "channels"))
if e, ok := err.(*os.PathError); ok && e.Err == syscall.ENOENT {
// The fixture for this interface doesn't exist. Translate that to unix.EOPNOTSUPP
// to replicate an interface that doesn't support ethtool driver info
return res, unix.EOPNOTSUPP
}
if err != nil {
return res, err
}
defer fixtureFile.Close()
scanner := bufio.NewScanner(fixtureFile)
currentConfig := false
for scanner.Scan() {
line := scanner.Text()
if strings.HasPrefix(line, "#") || strings.HasPrefix(line, "Channel parameters for") || strings.HasPrefix(line, "Pre-set maximums") {
continue
}
line = strings.Trim(line, " ")
items := strings.Split(line, ":")
fmt.Printf("line: %s\n", line)
fmt.Printf("items: %s\n", strings.Join(items, ","))
switch items[0] {
case "Current hardware settings":
currentConfig = true
case "RX":
if currentConfig {
res.RxCount = readChannel(items[1])
} else {
res.MaxRx = readChannel(items[1])
}
case "TX":
if currentConfig {
res.TxCount = readChannel(items[1])
} else {
res.MaxTx = readChannel(items[1])
}
case "Other":
if currentConfig {
res.OtherCount = readChannel(items[1])
} else {
res.MaxOther = readChannel(items[1])
}
case "Combined":
if currentConfig {
res.CombinedCount = readChannel(items[1])
} else {
res.MaxCombined = readChannel(items[1])
}
}
}
return res, nil
}
func readChannel(val string) uint32 {
val = strings.TrimSpace(val)
if val == "n/a" {
return 0
}
intVal, err := strconv.ParseUint(val, 10, 32)
if err != nil {
return 0
}
return uint32(intVal)
}
func NewEthtoolTestCollector(logger *slog.Logger) (Collector, error) {
collector, err := makeEthtoolCollector(logger)
if err != nil {
@ -291,6 +366,18 @@ func TestEthToolCollector(t *testing.T) {
testcase := `# HELP node_ethtool_align_errors Network interface align_errors
# TYPE node_ethtool_align_errors untyped
node_ethtool_align_errors{device="eth0"} 0
# HELP node_ethtool_channels_current Currently configured network interface channels
# TYPE node_ethtool_channels_current gauge
node_ethtool_channels_current{device="eth0",type="combined"} 128
node_ethtool_channels_current{device="eth0",type="other"} 1
node_ethtool_channels_current{device="eth0",type="rx"} 0
node_ethtool_channels_current{device="eth0",type="tx"} 0
# HELP node_ethtool_channels_max Maximum supported network interface channels
# TYPE node_ethtool_channels_max gauge
node_ethtool_channels_max{device="eth0",type="combined"} 252
node_ethtool_channels_max{device="eth0",type="other"} 1
node_ethtool_channels_max{device="eth0",type="rx"} 252
node_ethtool_channels_max{device="eth0",type="tx"} 252
# HELP node_ethtool_info A metric with a constant '1' value labeled by bus_info, device, driver, expansion_rom_version, firmware_version, version.
# TYPE node_ethtool_info gauge
node_ethtool_info{bus_info="0000:00:1f.6",device="eth0",driver="e1000e",expansion_rom_version="",firmware_version="0.5-4",version="5.11.0-22-generic"} 1

@ -0,0 +1,13 @@
# ethtool --show-channels eth0
Channel parameters for eth0:
Pre-set maximums:
RX: 252
TX: 252
Other: 1
Combined: 252
Current hardware settings:
# Testing n/a (which will be translated into 0)
RX: n/a
TX: 0
Other: 1
Combined: 128
Loading…
Cancel
Save