From 77a3c9bc20573bcea5760e85e9e610ff8e8023b6 Mon Sep 17 00:00:00 2001 From: "W. Andrew Denton" Date: Fri, 24 Sep 2021 09:56:09 -0700 Subject: [PATCH] Gather additional link info from ethtool. The ethtool_cmd struct from the linux kernel contains information about the speeds and features supported by a network device. This includes speeds and duplex but also features like autonegotiate and 802.3x pause frames. Closes #1444 Signed-off-by: W. Andrew Denton --- README.md | 2 +- collector/ethtool_linux.go | 207 +++++++++++++++++ collector/ethtool_linux_test.go | 274 ++++++++++++++++++++--- collector/fixtures/ethtool/eth0/settings | 26 +++ 4 files changed, 476 insertions(+), 33 deletions(-) create mode 100644 collector/fixtures/ethtool/eth0/settings diff --git a/README.md b/README.md index 0971fa81..cbfa202d 100644 --- a/README.md +++ b/README.md @@ -194,7 +194,7 @@ Name | Description | OS buddyinfo | Exposes statistics of memory fragments as reported by /proc/buddyinfo. | Linux devstat | Exposes device statistics | Dragonfly, FreeBSD drbd | Exposes Distributed Replicated Block Device statistics (to version 8.4) | Linux -ethtool | Exposes network interface and network driver statistics equivalent to `ethtool -S` and `ethtool -i`. | Linux +ethtool | Exposes network interface information and network driver statistics equivalent to `ethtool`, `ethtool -S`, and `ethtool -i`. | Linux interrupts | Exposes detailed interrupts statistics. | Linux, OpenBSD ksmd | Exposes kernel and system statistics from `/sys/kernel/mm/ksm`. | Linux logind | Exposes session counts from [logind](http://www.freedesktop.org/wiki/Software/systemd/logind/). | Linux diff --git a/collector/ethtool_linux.go b/collector/ethtool_linux.go index cbe31097..3116f826 100644 --- a/collector/ethtool_linux.go +++ b/collector/ethtool_linux.go @@ -38,6 +38,12 @@ import ( "gopkg.in/alecthomas/kingpin.v2" ) +const ( + // These are in _bytes_ to match bytes-per-second speeds from netclass. + Mbps = 1000000.0 / 8.0 + Gbps = 1000.0 * Mbps +) + var ( ethtoolDeviceInclude = kingpin.Flag("collector.ethtool.device-include", "Regexp of ethtool devices to include (mutually exclusive to device-exclude).").String() ethtoolDeviceExclude = kingpin.Flag("collector.ethtool.device-exclude", "Regexp of ethtool devices to exclude (mutually exclusive to device-include).").String() @@ -49,6 +55,7 @@ var ( type Ethtool interface { DriverInfo(string) (ethtool.DrvInfo, error) Stats(string) (map[string]uint64, error) + LinkInfo(string) (ethtool.EthtoolCmd, error) } type ethtoolLibrary struct { @@ -63,6 +70,12 @@ func (e *ethtoolLibrary) Stats(intf string) (map[string]uint64, error) { return e.ethtool.Stats(intf) } +func (e *ethtoolLibrary) LinkInfo(intf string) (ethtool.EthtoolCmd, error) { + var ethtoolCmd ethtool.EthtoolCmd + _, err := ethtoolCmd.CmdGet(intf) + return ethtoolCmd, err +} + type ethtoolCollector struct { fs sysfs.FS entries map[string]*prometheus.Desc @@ -130,6 +143,58 @@ func makeEthtoolCollector(logger log.Logger) (*ethtoolCollector, error) { "Network interface packets sent", []string{"device"}, nil, ), + + // link info + "supported_port": prometheus.NewDesc( + prometheus.BuildFQName(namespace, "network", "supported_port_info"), + "Type of ports or PHYs supported by network device", + []string{"device", "type"}, nil, + ), + "supported_speed": prometheus.NewDesc( + prometheus.BuildFQName(namespace, "network", "supported_speed_bytes"), + "Combination of speeds and features supported by network device", + []string{"device", "duplex", "mode"}, nil, + ), + "supported_autonegotiate": prometheus.NewDesc( + prometheus.BuildFQName(namespace, "network", "autonegotiate_supported"), + "If this port device supports autonegotiate", + []string{"device"}, nil, + ), + "supported_pause": prometheus.NewDesc( + prometheus.BuildFQName(namespace, "network", "pause_supported"), + "If this port device supports pause frames", + []string{"device"}, nil, + ), + "supported_asymmetricpause": prometheus.NewDesc( + prometheus.BuildFQName(namespace, "network", "asymmetricpause_supported"), + "If this port device supports asymmetric pause frames", + []string{"device"}, nil, + ), + "advertised_speed": prometheus.NewDesc( + prometheus.BuildFQName(namespace, "network", "advertised_speed_bytes"), + "Combination of speeds and features offered by network device", + []string{"device", "duplex", "mode"}, nil, + ), + "advertised_autonegotiate": prometheus.NewDesc( + prometheus.BuildFQName(namespace, "network", "autonegotiate_advertised"), + "If this port device offers autonegotiate", + []string{"device"}, nil, + ), + "advertised_pause": prometheus.NewDesc( + prometheus.BuildFQName(namespace, "network", "pause_advertised"), + "If this port device offers pause capability", + []string{"device"}, nil, + ), + "advertised_asymmetricpause": prometheus.NewDesc( + prometheus.BuildFQName(namespace, "network", "asymmetricpause_advertised"), + "If this port device offers asymmetric pause capability", + []string{"device"}, nil, + ), + "autonegotiate": prometheus.NewDesc( + prometheus.BuildFQName(namespace, "network", "autonegotiate"), + "If this port is using autonegotiate", + []string{"device"}, nil, + ), }, infoDesc: prometheus.NewDesc( prometheus.BuildFQName(namespace, "ethtool", "info"), @@ -156,6 +221,128 @@ func NewEthtoolCollector(logger log.Logger) (Collector, error) { return makeEthtoolCollector(logger) } +// updatePortCapabilities generates metrics for autonegotiate, pause and asymmetricpause. +// The bit offsets here correspond to ethtool_link_mode_bit_indices in linux/include/uapi/linux/ethtool.h +// https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/ethtool.h +func (c *ethtoolCollector) updatePortCapabilities(ch chan<- prometheus.Metric, prefix string, device string, linkModes uint32) { + autonegotiate := 0.0 + pause := 0.0 + asymmetricPause := 0.0 + if linkModes&(1<<6) != 0 { + autonegotiate = 1.0 + } + if linkModes&(1<<13) != 0 { + pause = 1.0 + } + if linkModes&(1<<14) != 0 { + asymmetricPause = 1.0 + } + ch <- prometheus.MustNewConstMetric(c.entries[fmt.Sprintf("%s_autonegotiate", prefix)], prometheus.GaugeValue, autonegotiate, device) + ch <- prometheus.MustNewConstMetric(c.entries[fmt.Sprintf("%s_pause", prefix)], prometheus.GaugeValue, pause, device) + ch <- prometheus.MustNewConstMetric(c.entries[fmt.Sprintf("%s_asymmetricpause", prefix)], prometheus.GaugeValue, asymmetricPause, device) +} + +// updatePortInfo generates port type metrics to indicate if the network devices supports Twisted Pair, optical fiber, etc. +// The bit offsets here correspond to ethtool_link_mode_bit_indices in linux/include/uapi/linux/ethtool.h +// https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/ethtool.h +func (c *ethtoolCollector) updatePortInfo(ch chan<- prometheus.Metric, device string, linkModes uint32) { + if linkModes&(1<<7) != 0 { + ch <- prometheus.MustNewConstMetric(c.entries["supported_port"], prometheus.GaugeValue, 1.0, device, "TP") + } + if linkModes&(1<<8) != 0 { + ch <- prometheus.MustNewConstMetric(c.entries["supported_port"], prometheus.GaugeValue, 1.0, device, "AUI") + } + if linkModes&(1<<9) != 0 { + ch <- prometheus.MustNewConstMetric(c.entries["supported_port"], prometheus.GaugeValue, 1.0, device, "MII") + } + if linkModes&(1<<10) != 0 { + ch <- prometheus.MustNewConstMetric(c.entries["supported_port"], prometheus.GaugeValue, 1.0, device, "FIBRE") + } + if linkModes&(1<<11) != 0 { + ch <- prometheus.MustNewConstMetric(c.entries["supported_port"], prometheus.GaugeValue, 1.0, device, "BNC") + } + if linkModes&(1<<16) != 0 { + ch <- prometheus.MustNewConstMetric(c.entries["supported_port"], prometheus.GaugeValue, 1.0, device, "Backplane") + } +} + +// updateSpeeds generates metrics corresponding to the speeds and duplex modes supported or advertised by the network device. +// The bit offsets here correspond to ethtool_link_mode_bit_indices in linux/include/uapi/linux/ethtool.h +// https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/ethtool.h +func (c *ethtoolCollector) updateSpeeds(ch chan<- prometheus.Metric, prefix string, device string, linkModes uint32) { + linkMode := fmt.Sprintf("%s_speed", prefix) + + if linkModes&(1<<0) != 0 { + ch <- prometheus.MustNewConstMetric(c.entries[linkMode], prometheus.GaugeValue, 10*Mbps, device, "half", "10baseT") + } + if linkModes&(1<<1) != 0 { + ch <- prometheus.MustNewConstMetric(c.entries[linkMode], prometheus.GaugeValue, 10*Mbps, device, "full", "10baseT") + } + if linkModes&(1<<2) != 0 { + ch <- prometheus.MustNewConstMetric(c.entries[linkMode], prometheus.GaugeValue, 100*Mbps, device, "half", "100baseT") + } + if linkModes&(1<<3) != 0 { + ch <- prometheus.MustNewConstMetric(c.entries[linkMode], prometheus.GaugeValue, 100*Mbps, device, "full", "100baseT") + } + if linkModes&(1<<4) != 0 { + ch <- prometheus.MustNewConstMetric(c.entries[linkMode], prometheus.GaugeValue, 1000*Mbps, device, "half", "1000baseT") + } + if linkModes&(1<<5) != 0 { + ch <- prometheus.MustNewConstMetric(c.entries[linkMode], prometheus.GaugeValue, 1000*Mbps, device, "full", "1000baseT") + } + if linkModes&(1<<12) != 0 { + ch <- prometheus.MustNewConstMetric(c.entries[linkMode], prometheus.GaugeValue, 10*Gbps, device, "full", "10000baseT") + } + if linkModes&(1<<15) != 0 { + ch <- prometheus.MustNewConstMetric(c.entries[linkMode], prometheus.GaugeValue, 2.5*Gbps, device, "full", "2500baseX") + } + if linkModes&(1<<17) != 0 { + ch <- prometheus.MustNewConstMetric(c.entries[linkMode], prometheus.GaugeValue, 1*Gbps, device, "full", "1000baseKX") + } + if linkModes&(1<<18) != 0 { + ch <- prometheus.MustNewConstMetric(c.entries[linkMode], prometheus.GaugeValue, 10*Gbps, device, "full", "10000baseKX4") + } + if linkModes&(1<<19) != 0 { + ch <- prometheus.MustNewConstMetric(c.entries[linkMode], prometheus.GaugeValue, 10*Gbps, device, "full", "10000baseKR") + } + if linkModes&(1<<20) != 0 { + ch <- prometheus.MustNewConstMetric(c.entries[linkMode], prometheus.GaugeValue, 10*Gbps, device, "full", "10000baseR_FEC") + } + if linkModes&(1<<21) != 0 { + ch <- prometheus.MustNewConstMetric(c.entries[linkMode], prometheus.GaugeValue, 20*Gbps, device, "full", "20000baseMLD2") + } + if linkModes&(1<<22) != 0 { + ch <- prometheus.MustNewConstMetric(c.entries[linkMode], prometheus.GaugeValue, 20*Gbps, device, "full", "20000baseKR2") + } + if linkModes&(1<<23) != 0 { + ch <- prometheus.MustNewConstMetric(c.entries[linkMode], prometheus.GaugeValue, 40*Gbps, device, "full", "40000baseKR4") + } + if linkModes&(1<<24) != 0 { + ch <- prometheus.MustNewConstMetric(c.entries[linkMode], prometheus.GaugeValue, 40*Gbps, device, "full", "40000baseCR4") + } + if linkModes&(1<<25) != 0 { + ch <- prometheus.MustNewConstMetric(c.entries[linkMode], prometheus.GaugeValue, 40*Gbps, device, "full", "40000baseSR4") + } + if linkModes&(1<<26) != 0 { + ch <- prometheus.MustNewConstMetric(c.entries[linkMode], prometheus.GaugeValue, 40*Gbps, device, "full", "40000baseLR4") + } + if linkModes&(1<<27) != 0 { + ch <- prometheus.MustNewConstMetric(c.entries[linkMode], prometheus.GaugeValue, 56*Gbps, device, "full", "56000baseKR4") + } + if linkModes&(1<<28) != 0 { + ch <- prometheus.MustNewConstMetric(c.entries[linkMode], prometheus.GaugeValue, 56*Gbps, device, "full", "56000baseCR4") + } + if linkModes&(1<<29) != 0 { + ch <- prometheus.MustNewConstMetric(c.entries[linkMode], prometheus.GaugeValue, 56*Gbps, device, "full", "56000baseSR4") + } + if linkModes&(1<<30) != 0 { + ch <- prometheus.MustNewConstMetric(c.entries[linkMode], prometheus.GaugeValue, 56*Gbps, device, "full", "56000baseLR4") + } + if linkModes&(1<<31) != 0 { + ch <- prometheus.MustNewConstMetric(c.entries[linkMode], prometheus.GaugeValue, 25*Gbps, device, "full", "25000baseCR") + } +} + func (c *ethtoolCollector) Update(ch chan<- prometheus.Metric) error { netClass, err := c.fs.NetClass() if err != nil { @@ -178,6 +365,26 @@ func (c *ethtoolCollector) Update(ch chan<- prometheus.Metric) error { continue } + linkInfo, err := c.ethtool.LinkInfo(device) + if err == nil { + c.updateSpeeds(ch, "supported", device, linkInfo.Supported) + c.updatePortInfo(ch, device, linkInfo.Supported) + c.updatePortCapabilities(ch, "supported", device, linkInfo.Supported) + c.updateSpeeds(ch, "advertised", device, linkInfo.Advertising) + c.updatePortCapabilities(ch, "advertised", device, linkInfo.Advertising) + ch <- prometheus.MustNewConstMetric(c.entries["autonegotiate"], prometheus.GaugeValue, float64(linkInfo.Autoneg), device) + } else { + if errno, ok := err.(syscall.Errno); ok { + if err == unix.EOPNOTSUPP { + level.Debug(c.logger).Log("msg", "ethtool link info error", "err", err, "device", device, "errno", uint(errno)) + } else if errno != 0 { + level.Error(c.logger).Log("msg", "ethtool link info error", "err", err, "device", device, "errno", uint(errno)) + } + } else { + level.Error(c.logger).Log("msg", "ethtool link info error", "err", err, "device", device) + } + } + drvInfo, err := c.ethtool.DriverInfo(device) if err == nil { diff --git a/collector/ethtool_linux_test.go b/collector/ethtool_linux_test.go index d607f664..276c6cb7 100644 --- a/collector/ethtool_linux_test.go +++ b/collector/ethtool_linux_test.go @@ -25,6 +25,7 @@ import ( "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/testutil" "github.com/safchain/ethtool" "golang.org/x/sys/unix" ) @@ -33,6 +34,28 @@ type EthtoolFixture struct { fixturePath string } +type testEthtoolCollector struct { + dsc Collector +} + +func (c testEthtoolCollector) Collect(ch chan<- prometheus.Metric) { + c.dsc.Update(ch) +} + +func (c testEthtoolCollector) Describe(ch chan<- *prometheus.Desc) { + prometheus.DescribeByCollect(c, ch) +} + +func NewTestEthtoolCollector(logger log.Logger) (prometheus.Collector, error) { + dsc, err := NewEthtoolTestCollector(logger) + if err != nil { + return testEthtoolCollector{}, err + } + return testEthtoolCollector{ + dsc: dsc, + }, err +} + func (e *EthtoolFixture) DriverInfo(intf string) (ethtool.DrvInfo, error) { res := ethtool.DrvInfo{} @@ -110,6 +133,130 @@ func (e *EthtoolFixture) Stats(intf string) (map[string]uint64, error) { return res, err } +func readModes(modes string) uint32 { + var out uint32 + for _, mode := range strings.Split(modes, " ") { + if mode == "10baseT/Half" { + out |= (1 << 0) + } + if mode == "10baseT/Full" { + out |= (1 << 1) + } + if mode == "100baseT/Half" { + out |= (1 << 2) + } + if mode == "100baseT/Full" { + out |= (1 << 3) + } + if mode == "1000baseT/Half" { + out |= (1 << 4) + } + if mode == "1000baseT/Full" { + out |= (1 << 5) + } + if mode == "10000baseT/Full" { + out |= (1 << 12) + } + } + return out +} + +func readPortTypes(portTypes string) uint32 { + var out uint32 + for _, ptype := range strings.Split(portTypes, " ") { + ptype = strings.Trim(ptype, " \t") + if ptype == "TP" { + out |= (1 << 7) + } + if ptype == "MII" { + out |= (1 << 9) + } + } + return out +} + +func (e *EthtoolFixture) LinkInfo(intf string) (ethtool.EthtoolCmd, error) { + var res ethtool.EthtoolCmd + fixtureFile, err := os.Open(filepath.Join(e.fixturePath, intf, "settings")) + 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 stats + return res, unix.EOPNOTSUPP + } + if err != nil { + return res, err + } + defer fixtureFile.Close() + + scanner := bufio.NewScanner(fixtureFile) + readingSupportedLinkModes := false + readingAdvertisedLinkModes := false + for scanner.Scan() { + line := scanner.Text() + if strings.HasPrefix(line, "#") || strings.HasPrefix(line, "Settings for") { + continue + } + line = strings.Trim(line, " \t") + + if (readingAdvertisedLinkModes || readingSupportedLinkModes) && strings.Contains(line, ":") { + readingAdvertisedLinkModes = false + readingSupportedLinkModes = false + } + + if readingAdvertisedLinkModes { + res.Advertising |= readModes(line) + continue + } else if readingSupportedLinkModes { + res.Supported |= readModes(line) + continue + } + + items := strings.Split(line, ": ") + if items[0] == "Supported pause frame use" { + if items[1] == "Symmetric" { + res.Supported |= (1 << 13) + } else if items[1] == "Receive-only" { + res.Supported |= (1 << 14) + } + } + if items[0] == "Advertised pause frame use" { + if items[1] == "Symmetric" { + res.Advertising |= (1 << 13) + } else if items[1] == "Receive-only" { + res.Advertising |= (1 << 14) + } + } + if items[0] == "Supported ports" { + res.Supported |= readPortTypes(items[1]) + } + if items[0] == "Supported link modes" { + res.Supported |= readModes(items[1]) + readingSupportedLinkModes = true + } + if items[0] == "Advertised link modes" { + res.Advertising |= readModes(items[1]) + readingAdvertisedLinkModes = true + } + if items[0] == "Supports auto-negotiation" { + if items[1] == "Yes" { + res.Supported |= (1 << 6) + } + } + if items[0] == "Advertised auto-negotiation" { + if items[1] == "Yes" { + res.Advertising |= (1 << 6) + } + } + if items[0] == "Auto-negotiation" { + if items[1] == "on" { + res.Autoneg = 1 + } + } + } + + return res, err +} + func NewEthtoolTestCollector(logger log.Logger) (Collector, error) { collector, err := makeEthtoolCollector(logger) collector.ethtool = &EthtoolFixture{ @@ -138,30 +285,103 @@ func TestBuildEthtoolFQName(t *testing.T) { } } -func TestEthtoolCollector(t *testing.T) { - testcases := []string{ - prometheus.NewDesc("node_ethtool_info", - "A metric with a constant '1' value labeled by bus_info, device, driver, expansion_rom_version, firmware_version, version.", - []string{"bus_info", "device", "driver", "expansion_rom_version", "firmware_version", "version"}, nil).String(), - prometheus.NewDesc("node_ethtool_align_errors", "Network interface align_errors", []string{"device"}, nil).String(), - prometheus.NewDesc("node_ethtool_received_broadcast", "Network interface rx_broadcast", []string{"device"}, nil).String(), - prometheus.NewDesc("node_ethtool_received_errors_total", "Number of received frames with errors", []string{"device"}, nil).String(), - prometheus.NewDesc("node_ethtool_received_missed", "Network interface rx_missed", []string{"device"}, nil).String(), - prometheus.NewDesc("node_ethtool_received_multicast", "Network interface rx_multicast", []string{"device"}, nil).String(), - prometheus.NewDesc("node_ethtool_received_packets_total", "Network interface packets received", []string{"device"}, nil).String(), - prometheus.NewDesc("node_ethtool_received_unicast", "Network interface rx_unicast", []string{"device"}, nil).String(), - prometheus.NewDesc("node_ethtool_transmitted_aborted", "Network interface tx_aborted", []string{"device"}, nil).String(), - prometheus.NewDesc("node_ethtool_transmitted_errors_total", "Number of sent frames with errors", []string{"device"}, nil).String(), - prometheus.NewDesc("node_ethtool_transmitted_multi_collisions", "Network interface tx_multi_collisions", []string{"device"}, nil).String(), - prometheus.NewDesc("node_ethtool_transmitted_packets_total", "Network interface packets sent", []string{"device"}, nil).String(), - } - +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_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 +# HELP node_ethtool_received_broadcast Network interface rx_broadcast +# TYPE node_ethtool_received_broadcast untyped +node_ethtool_received_broadcast{device="eth0"} 5792 +# HELP node_ethtool_received_errors_total Number of received frames with errors +# TYPE node_ethtool_received_errors_total untyped +node_ethtool_received_errors_total{device="eth0"} 0 +# HELP node_ethtool_received_missed Network interface rx_missed +# TYPE node_ethtool_received_missed untyped +node_ethtool_received_missed{device="eth0"} 401 +# HELP node_ethtool_received_multicast Network interface rx_multicast +# TYPE node_ethtool_received_multicast untyped +node_ethtool_received_multicast{device="eth0"} 23973 +# HELP node_ethtool_received_packets_total Network interface packets received +# TYPE node_ethtool_received_packets_total untyped +node_ethtool_received_packets_total{device="eth0"} 1.260062e+06 +# HELP node_ethtool_received_unicast Network interface rx_unicast +# TYPE node_ethtool_received_unicast untyped +node_ethtool_received_unicast{device="eth0"} 1.230297e+06 +# HELP node_ethtool_transmitted_aborted Network interface tx_aborted +# TYPE node_ethtool_transmitted_aborted untyped +node_ethtool_transmitted_aborted{device="eth0"} 0 +# HELP node_ethtool_transmitted_errors_total Number of sent frames with errors +# TYPE node_ethtool_transmitted_errors_total untyped +node_ethtool_transmitted_errors_total{device="eth0"} 0 +# HELP node_ethtool_transmitted_multi_collisions Network interface tx_multi_collisions +# TYPE node_ethtool_transmitted_multi_collisions untyped +node_ethtool_transmitted_multi_collisions{device="eth0"} 0 +# HELP node_ethtool_transmitted_packets_total Network interface packets sent +# TYPE node_ethtool_transmitted_packets_total untyped +node_ethtool_transmitted_packets_total{device="eth0"} 961500 +# HELP node_ethtool_transmitted_single_collisions Network interface tx_single_collisions +# TYPE node_ethtool_transmitted_single_collisions untyped +node_ethtool_transmitted_single_collisions{device="eth0"} 0 +# HELP node_ethtool_transmitted_underrun Network interface tx_underrun +# TYPE node_ethtool_transmitted_underrun untyped +node_ethtool_transmitted_underrun{device="eth0"} 0 +# HELP node_network_advertised_speed_bytes Combination of speeds and features offered by network device +# TYPE node_network_advertised_speed_bytes gauge +node_network_advertised_speed_bytes{device="eth0",duplex="full",mode="1000baseT"} 1.25e+08 +node_network_advertised_speed_bytes{device="eth0",duplex="full",mode="100baseT"} 1.25e+07 +node_network_advertised_speed_bytes{device="eth0",duplex="full",mode="10baseT"} 1.25e+06 +node_network_advertised_speed_bytes{device="eth0",duplex="half",mode="100baseT"} 1.25e+07 +node_network_advertised_speed_bytes{device="eth0",duplex="half",mode="10baseT"} 1.25e+06 +# HELP node_network_asymmetricpause_advertised If this port device offers asymmetric pause capability +# TYPE node_network_asymmetricpause_advertised gauge +node_network_asymmetricpause_advertised{device="eth0"} 0 +# HELP node_network_asymmetricpause_supported If this port device supports asymmetric pause frames +# TYPE node_network_asymmetricpause_supported gauge +node_network_asymmetricpause_supported{device="eth0"} 0 +# HELP node_network_autonegotiate If this port is using autonegotiate +# TYPE node_network_autonegotiate gauge +node_network_autonegotiate{device="eth0"} 1 +# HELP node_network_autonegotiate_advertised If this port device offers autonegotiate +# TYPE node_network_autonegotiate_advertised gauge +node_network_autonegotiate_advertised{device="eth0"} 1 +# HELP node_network_autonegotiate_supported If this port device supports autonegotiate +# TYPE node_network_autonegotiate_supported gauge +node_network_autonegotiate_supported{device="eth0"} 1 +# HELP node_network_pause_advertised If this port device offers pause capability +# TYPE node_network_pause_advertised gauge +node_network_pause_advertised{device="eth0"} 1 +# HELP node_network_pause_supported If this port device supports pause frames +# TYPE node_network_pause_supported gauge +node_network_pause_supported{device="eth0"} 1 +# HELP node_network_supported_port_info Type of ports or PHYs supported by network device +# TYPE node_network_supported_port_info gauge +node_network_supported_port_info{device="eth0",type="MII"} 1 +node_network_supported_port_info{device="eth0",type="TP"} 1 +# HELP node_network_supported_speed_bytes Combination of speeds and features supported by network device +# TYPE node_network_supported_speed_bytes gauge +node_network_supported_speed_bytes{device="eth0",duplex="full",mode="10000baseT"} 1.25e+09 +node_network_supported_speed_bytes{device="eth0",duplex="full",mode="1000baseT"} 1.25e+08 +node_network_supported_speed_bytes{device="eth0",duplex="full",mode="100baseT"} 1.25e+07 +node_network_supported_speed_bytes{device="eth0",duplex="full",mode="10baseT"} 1.25e+06 +node_network_supported_speed_bytes{device="eth0",duplex="half",mode="100baseT"} 1.25e+07 +node_network_supported_speed_bytes{device="eth0",duplex="half",mode="10baseT"} 1.25e+06 +` *sysPath = "fixtures/sys" - collector, err := NewEthtoolTestCollector(log.NewNopLogger()) + logger := log.NewLogfmtLogger(os.Stderr) + collector, err := NewEthtoolTestCollector(logger) if err != nil { panic(err) } + c, err := NewTestEthtoolCollector(logger) + if err != nil { + t.Fatal(err) + } + reg := prometheus.NewRegistry() + reg.MustRegister(c) sink := make(chan prometheus.Metric) go func() { @@ -172,18 +392,8 @@ func TestEthtoolCollector(t *testing.T) { close(sink) }() - for _, expected := range testcases { - metric := (<-sink) - if metric == nil { - t.Fatalf("Expected '%s' but got nothing (nil).", expected) - } - - got := metric.Desc().String() - metric.Desc() - if expected != got { - t.Errorf("Expected '%s' but got '%s'", expected, got) - } else { - t.Logf("Successfully got '%s'", got) - } + err = testutil.GatherAndCompare(reg, strings.NewReader(testcase)) + if err != nil { + t.Fatal(err) } } diff --git a/collector/fixtures/ethtool/eth0/settings b/collector/fixtures/ethtool/eth0/settings new file mode 100644 index 00000000..3404d255 --- /dev/null +++ b/collector/fixtures/ethtool/eth0/settings @@ -0,0 +1,26 @@ +# ethtool eth0 +Settings for eth0: + Supported ports: [ TP MII ] + Supported link modes: 10baseT/Half 10baseT/Full + 100baseT/Half 100baseT/Full + 1000baseT/Full 10000baseT/Full + Supported pause frame use: Symmetric + Supports auto-negotiation: Yes + Supported FEC modes: Not reported + Advertised link modes: 10baseT/Half 10baseT/Full + 100baseT/Half 100baseT/Full + 1000baseT/Full + Advertised pause frame use: Symmetric + Advertised auto-negotiation: Yes + Advertised FEC modes: Not reported + Speed: 1000Mb/s + Duplex: Full + Auto-negotiation: on + Port: Twisted Pair + PHYAD: 1 + Transceiver: internal + MDI-X: off (auto) +netlink error: Operation not permitted + Current message level: 0x00000007 (7) + drv probe link + Link detected: yes \ No newline at end of file