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 <git@flying-snail.net>
This commit is contained in:
W. Andrew Denton 2021-09-24 09:56:09 -07:00 committed by Johannes 'fish' Ziemke
parent 11bb2f8a95
commit 77a3c9bc20
4 changed files with 476 additions and 33 deletions

View file

@ -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

View file

@ -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 {

View file

@ -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)
}
}

View file

@ -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