From d063dc2e58ea18780f85e87a649847c527c9b6c6 Mon Sep 17 00:00:00 2001 From: Simon Everts Date: Thu, 9 Mar 2023 11:59:04 +0100 Subject: [PATCH] =?UTF-8?q?feat(inputs.ethtool):=20Add=20support=20for=20l?= =?UTF-8?q?ink=20speed,=20duplex,=20autoneg=20and=E2=80=A6=20(#12814)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 2 +- go.sum | 4 +- plugins/inputs/ethtool/README.md | 4 +- plugins/inputs/ethtool/ethtool.go | 1 + plugins/inputs/ethtool/ethtool_linux.go | 14 ++++ plugins/inputs/ethtool/ethtool_test.go | 92 +++++++++++++++++++++-- plugins/inputs/ethtool/namespace.go | 1 + plugins/inputs/ethtool/namespace_linux.go | 31 ++++++++ 8 files changed, 138 insertions(+), 11 deletions(-) diff --git a/go.mod b/go.mod index 71a141256..2579cc071 100644 --- a/go.mod +++ b/go.mod @@ -147,7 +147,7 @@ require ( github.com/rabbitmq/amqp091-go v1.7.0 github.com/riemann/riemann-go-client v0.5.1-0.20211206220514-f58f10cdce16 github.com/robbiet480/go.nut v0.0.0-20220219091450-bd8f121e1fa1 - github.com/safchain/ethtool v0.0.0-20210803160452-9aa261dae9b1 + github.com/safchain/ethtool v0.2.0 github.com/sensu/sensu-go/api/core/v2 v2.15.0 github.com/shirou/gopsutil/v3 v3.22.12 github.com/showwin/speedtest-go v1.4.2 diff --git a/go.sum b/go.sum index 2f6edfaa1..0b79624e6 100644 --- a/go.sum +++ b/go.sum @@ -2041,8 +2041,8 @@ github.com/ryanrolds/sqlclosecheck v0.3.0/go.mod h1:1gREqxyTGR3lVtpngyFo3hZAgk0K github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= -github.com/safchain/ethtool v0.0.0-20210803160452-9aa261dae9b1 h1:ZFfeKAhIQiiOrQaI3/znw0gOmYpO28Tcu1YaqMa/jtQ= -github.com/safchain/ethtool v0.0.0-20210803160452-9aa261dae9b1/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= +github.com/safchain/ethtool v0.2.0 h1:dILxMBqDnQfX192cCAPjZr9v2IgVXeElHPy435Z/IdE= +github.com/safchain/ethtool v0.2.0/go.mod h1:WkKB1DnNtvsMlDmQ50sgwowDJV/hGbJSOvJoEXs1AJQ= github.com/sagikazarmark/crypt v0.5.0/go.mod h1:l+nzl7KWh51rpzp2h7t4MZWyiEWdhNpOAnclKvg+mdA= github.com/sagikazarmark/crypt v0.6.0/go.mod h1:U8+INwJo3nBv1m6A/8OBXAq7Jnpspk5AxSgDyEQcea8= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= diff --git a/plugins/inputs/ethtool/README.md b/plugins/inputs/ethtool/README.md index 7c1187b07..e89359da0 100644 --- a/plugins/inputs/ethtool/README.md +++ b/plugins/inputs/ethtool/README.md @@ -101,6 +101,6 @@ Metrics are dependent on the network device and driver. ## Example Output ```shell -ethtool,driver=igb,host=test01,interface=mgmt0 tx_queue_1_packets=280782i,rx_queue_5_csum_err=0i,tx_queue_4_restart=0i,tx_multicast=7i,tx_queue_1_bytes=39674885i,rx_queue_2_alloc_failed=0i,tx_queue_5_packets=173970i,tx_single_coll_ok=0i,rx_queue_1_drops=0i,tx_queue_2_restart=0i,tx_aborted_errors=0i,rx_queue_6_csum_err=0i,tx_queue_5_restart=0i,tx_queue_4_bytes=64810835i,tx_abort_late_coll=0i,tx_queue_4_packets=109102i,os2bmc_tx_by_bmc=0i,tx_bytes=427527435i,tx_queue_7_packets=66665i,dropped_smbus=0i,rx_queue_0_csum_err=0i,tx_flow_control_xoff=0i,rx_packets=25926536i,rx_queue_7_csum_err=0i,rx_queue_3_bytes=84326060i,rx_multicast=83771i,rx_queue_4_alloc_failed=0i,rx_queue_3_drops=0i,rx_queue_3_csum_err=0i,rx_errors=0i,tx_errors=0i,tx_queue_6_packets=183236i,rx_broadcast=24378893i,rx_queue_7_packets=88680i,tx_dropped=0i,rx_frame_errors=0i,tx_queue_3_packets=161045i,tx_packets=1257017i,rx_queue_1_csum_err=0i,tx_window_errors=0i,tx_dma_out_of_sync=0i,rx_length_errors=0i,rx_queue_5_drops=0i,tx_timeout_count=0i,rx_queue_4_csum_err=0i,rx_flow_control_xon=0i,tx_heartbeat_errors=0i,tx_flow_control_xon=0i,collisions=0i,tx_queue_0_bytes=29465801i,rx_queue_6_drops=0i,rx_queue_0_alloc_failed=0i,tx_queue_1_restart=0i,rx_queue_0_drops=0i,tx_broadcast=9i,tx_carrier_errors=0i,tx_queue_7_bytes=13777515i,tx_queue_7_restart=0i,rx_queue_5_bytes=50732006i,rx_queue_7_bytes=35744457i,tx_deferred_ok=0i,tx_multi_coll_ok=0i,rx_crc_errors=0i,rx_fifo_errors=0i,rx_queue_6_alloc_failed=0i,tx_queue_2_packets=175206i,tx_queue_0_packets=107011i,rx_queue_4_bytes=201364548i,rx_queue_6_packets=372573i,os2bmc_rx_by_host=0i,multicast=83771i,rx_queue_4_drops=0i,rx_queue_5_packets=130535i,rx_queue_6_bytes=139488035i,tx_fifo_errors=0i,tx_queue_5_bytes=84899130i,rx_queue_0_packets=24529563i,rx_queue_3_alloc_failed=0i,rx_queue_7_drops=0i,tx_queue_6_bytes=96288614i,tx_queue_2_bytes=22132949i,tx_tcp_seg_failed=0i,rx_queue_1_bytes=246703840i,rx_queue_0_bytes=1506870738i,tx_queue_0_restart=0i,rx_queue_2_bytes=111344804i,tx_tcp_seg_good=0i,tx_queue_3_restart=0i,rx_no_buffer_count=0i,rx_smbus=0i,rx_queue_1_packets=273865i,rx_over_errors=0i,os2bmc_tx_by_host=0i,rx_queue_1_alloc_failed=0i,rx_queue_7_alloc_failed=0i,rx_short_length_errors=0i,tx_hwtstamp_timeouts=0i,tx_queue_6_restart=0i,rx_queue_2_packets=207136i,tx_queue_3_bytes=70391970i,rx_queue_3_packets=112007i,rx_queue_4_packets=212177i,tx_smbus=0i,rx_long_byte_count=2480280632i,rx_queue_2_csum_err=0i,rx_missed_errors=0i,rx_bytes=2480280632i,rx_queue_5_alloc_failed=0i,rx_queue_2_drops=0i,os2bmc_rx_by_bmc=0i,rx_align_errors=0i,rx_long_length_errors=0i,interface_up=1i,rx_hwtstamp_cleared=0i,rx_flow_control_xoff=0i 1564658080000000000 -ethtool,driver=igb,host=test02,interface=mgmt0 rx_queue_2_bytes=111344804i,tx_queue_3_bytes=70439858i,multicast=83771i,rx_broadcast=24378975i,tx_queue_0_packets=107011i,rx_queue_6_alloc_failed=0i,rx_queue_6_drops=0i,rx_hwtstamp_cleared=0i,tx_window_errors=0i,tx_tcp_seg_good=0i,rx_queue_1_drops=0i,tx_queue_1_restart=0i,rx_queue_7_csum_err=0i,rx_no_buffer_count=0i,tx_queue_1_bytes=39675245i,tx_queue_5_bytes=84899130i,tx_broadcast=9i,rx_queue_1_csum_err=0i,tx_flow_control_xoff=0i,rx_queue_6_csum_err=0i,tx_timeout_count=0i,os2bmc_tx_by_bmc=0i,rx_queue_6_packets=372577i,rx_queue_0_alloc_failed=0i,tx_flow_control_xon=0i,rx_queue_2_drops=0i,tx_queue_2_packets=175206i,rx_queue_3_csum_err=0i,tx_abort_late_coll=0i,tx_queue_5_restart=0i,tx_dropped=0i,rx_queue_2_alloc_failed=0i,tx_multi_coll_ok=0i,rx_queue_1_packets=273865i,rx_flow_control_xon=0i,tx_single_coll_ok=0i,rx_length_errors=0i,rx_queue_7_bytes=35744457i,rx_queue_4_alloc_failed=0i,rx_queue_6_bytes=139488395i,rx_queue_2_csum_err=0i,rx_long_byte_count=2480288216i,rx_queue_1_alloc_failed=0i,tx_queue_0_restart=0i,rx_queue_0_csum_err=0i,tx_queue_2_bytes=22132949i,rx_queue_5_drops=0i,tx_dma_out_of_sync=0i,rx_queue_3_drops=0i,rx_queue_4_packets=212177i,tx_queue_6_restart=0i,rx_packets=25926650i,rx_queue_7_packets=88680i,rx_frame_errors=0i,rx_queue_3_bytes=84326060i,rx_short_length_errors=0i,tx_queue_7_bytes=13777515i,rx_queue_3_alloc_failed=0i,tx_queue_6_packets=183236i,rx_queue_0_drops=0i,rx_multicast=83771i,rx_queue_2_packets=207136i,rx_queue_5_csum_err=0i,rx_queue_5_packets=130535i,rx_queue_7_alloc_failed=0i,tx_smbus=0i,tx_queue_3_packets=161081i,rx_queue_7_drops=0i,tx_queue_2_restart=0i,tx_multicast=7i,tx_fifo_errors=0i,tx_queue_3_restart=0i,rx_long_length_errors=0i,tx_queue_6_bytes=96288614i,tx_queue_1_packets=280786i,tx_tcp_seg_failed=0i,rx_align_errors=0i,tx_errors=0i,rx_crc_errors=0i,rx_queue_0_packets=24529673i,rx_flow_control_xoff=0i,tx_queue_0_bytes=29465801i,rx_over_errors=0i,rx_queue_4_drops=0i,os2bmc_rx_by_bmc=0i,rx_smbus=0i,dropped_smbus=0i,tx_hwtstamp_timeouts=0i,rx_errors=0i,tx_queue_4_packets=109102i,tx_carrier_errors=0i,tx_queue_4_bytes=64810835i,tx_queue_4_restart=0i,rx_queue_4_csum_err=0i,tx_queue_7_packets=66665i,tx_aborted_errors=0i,rx_missed_errors=0i,tx_bytes=427575843i,collisions=0i,rx_queue_1_bytes=246703840i,rx_queue_5_bytes=50732006i,rx_bytes=2480288216i,os2bmc_rx_by_host=0i,rx_queue_5_alloc_failed=0i,rx_queue_3_packets=112007i,tx_deferred_ok=0i,os2bmc_tx_by_host=0i,tx_heartbeat_errors=0i,rx_queue_0_bytes=1506877506i,tx_queue_7_restart=0i,tx_packets=1257057i,rx_queue_4_bytes=201364548i,interface_up=0i,rx_fifo_errors=0i,tx_queue_5_packets=173970i 1564658090000000000 +ethtool,driver=igb,host=test01,interface=mgmt0 tx_queue_1_packets=280782i,rx_queue_5_csum_err=0i,tx_queue_4_restart=0i,tx_multicast=7i,tx_queue_1_bytes=39674885i,rx_queue_2_alloc_failed=0i,tx_queue_5_packets=173970i,tx_single_coll_ok=0i,rx_queue_1_drops=0i,tx_queue_2_restart=0i,tx_aborted_errors=0i,rx_queue_6_csum_err=0i,tx_queue_5_restart=0i,tx_queue_4_bytes=64810835i,tx_abort_late_coll=0i,tx_queue_4_packets=109102i,os2bmc_tx_by_bmc=0i,tx_bytes=427527435i,tx_queue_7_packets=66665i,dropped_smbus=0i,rx_queue_0_csum_err=0i,tx_flow_control_xoff=0i,rx_packets=25926536i,rx_queue_7_csum_err=0i,rx_queue_3_bytes=84326060i,rx_multicast=83771i,rx_queue_4_alloc_failed=0i,rx_queue_3_drops=0i,rx_queue_3_csum_err=0i,rx_errors=0i,tx_errors=0i,tx_queue_6_packets=183236i,rx_broadcast=24378893i,rx_queue_7_packets=88680i,tx_dropped=0i,rx_frame_errors=0i,tx_queue_3_packets=161045i,tx_packets=1257017i,rx_queue_1_csum_err=0i,tx_window_errors=0i,tx_dma_out_of_sync=0i,rx_length_errors=0i,rx_queue_5_drops=0i,tx_timeout_count=0i,rx_queue_4_csum_err=0i,rx_flow_control_xon=0i,tx_heartbeat_errors=0i,tx_flow_control_xon=0i,collisions=0i,tx_queue_0_bytes=29465801i,rx_queue_6_drops=0i,rx_queue_0_alloc_failed=0i,tx_queue_1_restart=0i,rx_queue_0_drops=0i,tx_broadcast=9i,tx_carrier_errors=0i,tx_queue_7_bytes=13777515i,tx_queue_7_restart=0i,rx_queue_5_bytes=50732006i,rx_queue_7_bytes=35744457i,tx_deferred_ok=0i,tx_multi_coll_ok=0i,rx_crc_errors=0i,rx_fifo_errors=0i,rx_queue_6_alloc_failed=0i,tx_queue_2_packets=175206i,tx_queue_0_packets=107011i,rx_queue_4_bytes=201364548i,rx_queue_6_packets=372573i,os2bmc_rx_by_host=0i,multicast=83771i,rx_queue_4_drops=0i,rx_queue_5_packets=130535i,rx_queue_6_bytes=139488035i,tx_fifo_errors=0i,tx_queue_5_bytes=84899130i,rx_queue_0_packets=24529563i,rx_queue_3_alloc_failed=0i,rx_queue_7_drops=0i,tx_queue_6_bytes=96288614i,tx_queue_2_bytes=22132949i,tx_tcp_seg_failed=0i,rx_queue_1_bytes=246703840i,rx_queue_0_bytes=1506870738i,tx_queue_0_restart=0i,rx_queue_2_bytes=111344804i,tx_tcp_seg_good=0i,tx_queue_3_restart=0i,rx_no_buffer_count=0i,rx_smbus=0i,rx_queue_1_packets=273865i,rx_over_errors=0i,os2bmc_tx_by_host=0i,rx_queue_1_alloc_failed=0i,rx_queue_7_alloc_failed=0i,rx_short_length_errors=0i,tx_hwtstamp_timeouts=0i,tx_queue_6_restart=0i,rx_queue_2_packets=207136i,tx_queue_3_bytes=70391970i,rx_queue_3_packets=112007i,rx_queue_4_packets=212177i,tx_smbus=0i,rx_long_byte_count=2480280632i,rx_queue_2_csum_err=0i,rx_missed_errors=0i,rx_bytes=2480280632i,rx_queue_5_alloc_failed=0i,rx_queue_2_drops=0i,os2bmc_rx_by_bmc=0i,rx_align_errors=0i,rx_long_length_errors=0i,interface_up=1i,rx_hwtstamp_cleared=0i,rx_flow_control_xoff=0i,speed=1000i,link=1i,duplex=1i,autoneg=1i 1564658080000000000 +ethtool,driver=igb,host=test02,interface=mgmt0 rx_queue_2_bytes=111344804i,tx_queue_3_bytes=70439858i,multicast=83771i,rx_broadcast=24378975i,tx_queue_0_packets=107011i,rx_queue_6_alloc_failed=0i,rx_queue_6_drops=0i,rx_hwtstamp_cleared=0i,tx_window_errors=0i,tx_tcp_seg_good=0i,rx_queue_1_drops=0i,tx_queue_1_restart=0i,rx_queue_7_csum_err=0i,rx_no_buffer_count=0i,tx_queue_1_bytes=39675245i,tx_queue_5_bytes=84899130i,tx_broadcast=9i,rx_queue_1_csum_err=0i,tx_flow_control_xoff=0i,rx_queue_6_csum_err=0i,tx_timeout_count=0i,os2bmc_tx_by_bmc=0i,rx_queue_6_packets=372577i,rx_queue_0_alloc_failed=0i,tx_flow_control_xon=0i,rx_queue_2_drops=0i,tx_queue_2_packets=175206i,rx_queue_3_csum_err=0i,tx_abort_late_coll=0i,tx_queue_5_restart=0i,tx_dropped=0i,rx_queue_2_alloc_failed=0i,tx_multi_coll_ok=0i,rx_queue_1_packets=273865i,rx_flow_control_xon=0i,tx_single_coll_ok=0i,rx_length_errors=0i,rx_queue_7_bytes=35744457i,rx_queue_4_alloc_failed=0i,rx_queue_6_bytes=139488395i,rx_queue_2_csum_err=0i,rx_long_byte_count=2480288216i,rx_queue_1_alloc_failed=0i,tx_queue_0_restart=0i,rx_queue_0_csum_err=0i,tx_queue_2_bytes=22132949i,rx_queue_5_drops=0i,tx_dma_out_of_sync=0i,rx_queue_3_drops=0i,rx_queue_4_packets=212177i,tx_queue_6_restart=0i,rx_packets=25926650i,rx_queue_7_packets=88680i,rx_frame_errors=0i,rx_queue_3_bytes=84326060i,rx_short_length_errors=0i,tx_queue_7_bytes=13777515i,rx_queue_3_alloc_failed=0i,tx_queue_6_packets=183236i,rx_queue_0_drops=0i,rx_multicast=83771i,rx_queue_2_packets=207136i,rx_queue_5_csum_err=0i,rx_queue_5_packets=130535i,rx_queue_7_alloc_failed=0i,tx_smbus=0i,tx_queue_3_packets=161081i,rx_queue_7_drops=0i,tx_queue_2_restart=0i,tx_multicast=7i,tx_fifo_errors=0i,tx_queue_3_restart=0i,rx_long_length_errors=0i,tx_queue_6_bytes=96288614i,tx_queue_1_packets=280786i,tx_tcp_seg_failed=0i,rx_align_errors=0i,tx_errors=0i,rx_crc_errors=0i,rx_queue_0_packets=24529673i,rx_flow_control_xoff=0i,tx_queue_0_bytes=29465801i,rx_over_errors=0i,rx_queue_4_drops=0i,os2bmc_rx_by_bmc=0i,rx_smbus=0i,dropped_smbus=0i,tx_hwtstamp_timeouts=0i,rx_errors=0i,tx_queue_4_packets=109102i,tx_carrier_errors=0i,tx_queue_4_bytes=64810835i,tx_queue_4_restart=0i,rx_queue_4_csum_err=0i,tx_queue_7_packets=66665i,tx_aborted_errors=0i,rx_missed_errors=0i,tx_bytes=427575843i,collisions=0i,rx_queue_1_bytes=246703840i,rx_queue_5_bytes=50732006i,rx_bytes=2480288216i,os2bmc_rx_by_host=0i,rx_queue_5_alloc_failed=0i,rx_queue_3_packets=112007i,tx_deferred_ok=0i,os2bmc_tx_by_host=0i,tx_heartbeat_errors=0i,rx_queue_0_bytes=1506877506i,tx_queue_7_restart=0i,tx_packets=1257057i,rx_queue_4_bytes=201364548i,interface_up=0i,rx_fifo_errors=0i,tx_queue_5_packets=173970i,speed=1000i,link=1i,duplex=1i,autoneg=1i 1564658090000000000 ``` diff --git a/plugins/inputs/ethtool/ethtool.go b/plugins/inputs/ethtool/ethtool.go index 0429981cd..0e4f0b813 100644 --- a/plugins/inputs/ethtool/ethtool.go +++ b/plugins/inputs/ethtool/ethtool.go @@ -18,6 +18,7 @@ type Command interface { DriverName(intf NamespacedInterface) (string, error) Interfaces(includeNamespaces bool) ([]NamespacedInterface, error) Stats(intf NamespacedInterface) (map[string]uint64, error) + Get(intf NamespacedInterface) (map[string]uint64, error) } type Ethtool struct { diff --git a/plugins/inputs/ethtool/ethtool_linux.go b/plugins/inputs/ethtool/ethtool_linux.go index f46022abb..52bf33290 100644 --- a/plugins/inputs/ethtool/ethtool_linux.go +++ b/plugins/inputs/ethtool/ethtool_linux.go @@ -132,6 +132,16 @@ func (e *Ethtool) gatherEthtoolStats(iface NamespacedInterface, acc telegraf.Acc fields[e.normalizeKey(k)] = v } + cmdget, err := e.command.Get(iface) + if err != nil { + acc.AddError(fmt.Errorf("%q get: %w", iface.Name, err)) + return + } + + for k, v := range cmdget { + fields[e.normalizeKey(k)] = v + } + acc.AddFields(pluginName, fields, tags) } @@ -221,6 +231,10 @@ func (c *CommandEthtool) Stats(intf NamespacedInterface) (stats map[string]uint6 return intf.Namespace.Stats(intf) } +func (c *CommandEthtool) Get(intf NamespacedInterface) (stats map[string]uint64, err error) { + return intf.Namespace.Get(intf) +} + func (c *CommandEthtool) Interfaces(includeNamespaces bool) ([]NamespacedInterface, error) { const namespaceDirectory = "/var/run/netns" diff --git a/plugins/inputs/ethtool/ethtool_test.go b/plugins/inputs/ethtool/ethtool_test.go index 8ecc852e8..04bcf6ddb 100644 --- a/plugins/inputs/ethtool/ethtool_test.go +++ b/plugins/inputs/ethtool/ethtool_test.go @@ -24,6 +24,7 @@ type InterfaceMock struct { Stat map[string]uint64 LoopBack bool InterfaceUp bool + CmdGet map[string]uint64 } type NamespaceMock struct { @@ -46,6 +47,10 @@ func (n *NamespaceMock) Stats(_ NamespacedInterface) (map[string]uint64, error) return nil, errors.New("it is a test bug to invoke this function") } +func (n *NamespaceMock) Get(_ NamespacedInterface) (map[string]uint64, error) { + return nil, errors.New("it is a test bug to invoke this function") +} + type CommandEthtoolMock struct { InterfaceMap map[string]*InterfaceMock } @@ -117,6 +122,14 @@ func (c *CommandEthtoolMock) Stats(intf NamespacedInterface) (map[string]uint64, return nil, errors.New("interface not found") } +func (c *CommandEthtoolMock) Get(intf NamespacedInterface) (map[string]uint64, error) { + i := c.InterfaceMap[intf.Name] + if i != nil { + return i.CmdGet, nil + } + return nil, errors.New("interface not found") +} + func setup() { interfaceMap = make(map[string]*InterfaceMock) @@ -219,7 +232,13 @@ func setup() { "tx_tso_fallbacks": 0, "tx_tso_long_headers": 0, } - eth1 := &InterfaceMock{"eth1", "driver1", "", eth1Stat, false, true} + eth1Get := map[string]uint64{ + "autoneg": 1, + "duplex": 1, + "link": 1, + "speed": 1000, + } + eth1 := &InterfaceMock{"eth1", "driver1", "", eth1Stat, false, true, eth1Get} interfaceMap[eth1.Name] = eth1 eth2Stat := map[string]uint64{ @@ -321,7 +340,13 @@ func setup() { "tx_tso_fallbacks": 0, "tx_tso_long_headers": 0, } - eth2 := &InterfaceMock{"eth2", "driver1", "", eth2Stat, false, false} + eth2Get := map[string]uint64{ + "autoneg": 0, + "duplex": 255, + "link": 0, + "speed": 9223372036854775807, + } + eth2 := &InterfaceMock{"eth2", "driver1", "", eth2Stat, false, false, eth2Get} interfaceMap[eth2.Name] = eth2 eth3Stat := map[string]uint64{ @@ -423,7 +448,13 @@ func setup() { "tx_tso_fallbacks": 0, "tx_tso_long_headers": 0, } - eth3 := &InterfaceMock{"eth3", "driver1", "namespace1", eth3Stat, false, true} + eth3Get := map[string]uint64{ + "autoneg": 1, + "duplex": 1, + "link": 1, + "speed": 1000, + } + eth3 := &InterfaceMock{"eth3", "driver1", "namespace1", eth3Stat, false, true, eth3Get} interfaceMap[eth3.Name] = eth3 eth4Stat := map[string]uint64{ @@ -525,14 +556,26 @@ func setup() { "tx_tso_fallbacks": 0, "tx_tso_long_headers": 0, } - eth4 := &InterfaceMock{"eth4", "driver1", "namespace2", eth4Stat, false, true} + eth4Get := map[string]uint64{ + "autoneg": 1, + "duplex": 1, + "link": 1, + "speed": 100, + } + eth4 := &InterfaceMock{"eth4", "driver1", "namespace2", eth4Stat, false, true, eth4Get} interfaceMap[eth4.Name] = eth4 // dummy loopback including dummy stat to ensure that the ignore feature is working lo0Stat := map[string]uint64{ "dummy": 0, } - lo0 := &InterfaceMock{"lo0", "", "", lo0Stat, true, true} + lo0Get := map[string]uint64{ + "autoneg": 1, + "duplex": 1, + "link": 1, + "speed": 1000, + } + lo0 := &InterfaceMock{"lo0", "", "", lo0Stat, true, true, lo0Get} interfaceMap[lo0.Name] = lo0 c := &CommandEthtoolMock{interfaceMap} @@ -573,6 +616,9 @@ func TestGather(t *testing.T) { require.Len(t, acc.Metrics, 2) expectedFieldsEth1 := toStringMapInterface(interfaceMap["eth1"].Stat) + for k, v := range interfaceMap["eth1"].CmdGet { + expectedFieldsEth1[k] = v + } expectedFieldsEth1["interface_up_counter"] = expectedFieldsEth1["interface_up"] expectedFieldsEth1["interface_up"] = true @@ -582,7 +628,11 @@ func TestGather(t *testing.T) { "namespace": "", } acc.AssertContainsTaggedFields(t, pluginName, expectedFieldsEth1, expectedTagsEth1) + expectedFieldsEth2 := toStringMapInterface(interfaceMap["eth2"].Stat) + for k, v := range interfaceMap["eth2"].CmdGet { + expectedFieldsEth2[k] = v + } expectedFieldsEth2["interface_up_counter"] = expectedFieldsEth2["interface_up"] expectedFieldsEth2["interface_up"] = false expectedTagsEth2 := map[string]string{ @@ -608,6 +658,9 @@ func TestGatherIncludeInterfaces(t *testing.T) { // Should contain eth1 expectedFieldsEth1 := toStringMapInterface(interfaceMap["eth1"].Stat) + for k, v := range interfaceMap["eth1"].CmdGet { + expectedFieldsEth1[k] = v + } expectedFieldsEth1["interface_up_counter"] = expectedFieldsEth1["interface_up"] expectedFieldsEth1["interface_up"] = true expectedTagsEth1 := map[string]string{ @@ -619,6 +672,9 @@ func TestGatherIncludeInterfaces(t *testing.T) { // Should not contain eth2 expectedFieldsEth2 := toStringMapInterface(interfaceMap["eth2"].Stat) + for k, v := range interfaceMap["eth2"].CmdGet { + expectedFieldsEth2[k] = v + } expectedFieldsEth2["interface_up_counter"] = expectedFieldsEth2["interface_up"] expectedFieldsEth2["interface_up"] = false expectedTagsEth2 := map[string]string{ @@ -644,6 +700,9 @@ func TestGatherIgnoreInterfaces(t *testing.T) { // Should not contain eth1 expectedFieldsEth1 := toStringMapInterface(interfaceMap["eth1"].Stat) + for k, v := range interfaceMap["eth1"].CmdGet { + expectedFieldsEth1[k] = v + } expectedFieldsEth1["interface_up_counter"] = expectedFieldsEth1["interface_up"] expectedFieldsEth1["interface_up"] = true expectedTagsEth1 := map[string]string{ @@ -655,6 +714,9 @@ func TestGatherIgnoreInterfaces(t *testing.T) { // Should contain eth2 expectedFieldsEth2 := toStringMapInterface(interfaceMap["eth2"].Stat) + for k, v := range interfaceMap["eth2"].CmdGet { + expectedFieldsEth2[k] = v + } expectedFieldsEth2["interface_up_counter"] = expectedFieldsEth2["interface_up"] expectedFieldsEth2["interface_up"] = false expectedTagsEth2 := map[string]string{ @@ -679,6 +741,9 @@ func TestSkipMetricsForInterfaceDown(t *testing.T) { require.Len(t, acc.Metrics, 1) expectedFieldsEth1 := toStringMapInterface(interfaceMap["eth1"].Stat) + for k, v := range interfaceMap["eth1"].CmdGet { + expectedFieldsEth1[k] = v + } expectedFieldsEth1["interface_up_counter"] = expectedFieldsEth1["interface_up"] expectedFieldsEth1["interface_up"] = true @@ -705,6 +770,9 @@ func TestGatherIncludeNamespaces(t *testing.T) { // Should contain eth3 expectedFieldsEth3 := toStringMapInterface(interfaceMap["eth3"].Stat) + for k, v := range interfaceMap["eth3"].CmdGet { + expectedFieldsEth3[k] = v + } expectedFieldsEth3["interface_up_counter"] = expectedFieldsEth3["interface_up"] expectedFieldsEth3["interface_up"] = true expectedTagsEth3 := map[string]string{ @@ -716,6 +784,9 @@ func TestGatherIncludeNamespaces(t *testing.T) { // Should not contain eth2 expectedFieldsEth2 := toStringMapInterface(interfaceMap["eth2"].Stat) + for k, v := range interfaceMap["eth2"].CmdGet { + expectedFieldsEth2[k] = v + } expectedFieldsEth2["interface_up_counter"] = expectedFieldsEth2["interface_up"] expectedFieldsEth2["interface_up"] = false expectedTagsEth2 := map[string]string{ @@ -741,6 +812,9 @@ func TestGatherIgnoreNamespaces(t *testing.T) { // Should not contain eth4 expectedFieldsEth4 := toStringMapInterface(interfaceMap["eth4"].Stat) + for k, v := range interfaceMap["eth4"].CmdGet { + expectedFieldsEth4[k] = v + } expectedFieldsEth4["interface_up_counter"] = expectedFieldsEth4["interface_up"] expectedFieldsEth4["interface_up"] = true expectedTagsEth4 := map[string]string{ @@ -752,6 +826,9 @@ func TestGatherIgnoreNamespaces(t *testing.T) { // Should contain eth2 expectedFieldsEth2 := toStringMapInterface(interfaceMap["eth2"].Stat) + for k, v := range interfaceMap["eth2"].CmdGet { + expectedFieldsEth2[k] = v + } expectedFieldsEth2["interface_up_counter"] = expectedFieldsEth2["interface_up"] expectedFieldsEth2["interface_up"] = false expectedTagsEth2 := map[string]string{ @@ -763,6 +840,9 @@ func TestGatherIgnoreNamespaces(t *testing.T) { // Should contain eth3 expectedFieldsEth3 := toStringMapInterface(interfaceMap["eth3"].Stat) + for k, v := range interfaceMap["eth3"].CmdGet { + expectedFieldsEth3[k] = v + } expectedFieldsEth3["interface_up_counter"] = expectedFieldsEth3["interface_up"] expectedFieldsEth3["interface_up"] = true expectedTagsEth3 := map[string]string{ @@ -880,7 +960,7 @@ func TestNormalizedKeys(t *testing.T) { } for _, c := range cases { - eth0 := &InterfaceMock{"eth0", "e1000e", "", toStringMapUint(c.stats), false, true} + eth0 := &InterfaceMock{"eth0", "e1000e", "", toStringMapUint(c.stats), false, true, map[string]uint64{}} expectedTags := map[string]string{ "interface": eth0.Name, "driver": eth0.DriverName, diff --git a/plugins/inputs/ethtool/namespace.go b/plugins/inputs/ethtool/namespace.go index 948b460e4..c270ff00c 100644 --- a/plugins/inputs/ethtool/namespace.go +++ b/plugins/inputs/ethtool/namespace.go @@ -7,6 +7,7 @@ type Namespace interface { Interfaces() ([]NamespacedInterface, error) DriverName(intf NamespacedInterface) (string, error) Stats(intf NamespacedInterface) (map[string]uint64, error) + Get(intf NamespacedInterface) (map[string]uint64, error) } type NamespacedInterface struct { diff --git a/plugins/inputs/ethtool/namespace_linux.go b/plugins/inputs/ethtool/namespace_linux.go index f60b61cb4..a3d7d9e15 100644 --- a/plugins/inputs/ethtool/namespace_linux.go +++ b/plugins/inputs/ethtool/namespace_linux.go @@ -1,6 +1,7 @@ package ethtool import ( + "math" "net" "runtime" @@ -68,6 +69,36 @@ func (n *NamespaceGoroutine) Stats(intf NamespacedInterface) (map[string]uint64, return driver.(map[string]uint64), err } +func (n *NamespaceGoroutine) Get(intf NamespacedInterface) (map[string]uint64, error) { + result, err := n.Do(func(n *NamespaceGoroutine) (interface{}, error) { + ecmd := ethtoolLib.EthtoolCmd{} + speed32, err := n.ethtoolClient.CmdGet(&ecmd, intf.Name) + if err != nil { + return nil, err + } + + var speed = uint64(speed32) + if speed == math.MaxUint32 { + speed = math.MaxUint64 + } + + var link32 uint32 + link32, err = n.ethtoolClient.LinkState(intf.Name) + if err != nil { + return nil, err + } + + return map[string]uint64{ + "speed": speed, + "duplex": uint64(ecmd.Duplex), + "autoneg": uint64(ecmd.Autoneg), + "link": uint64(link32), + }, nil + }) + + return result.(map[string]uint64), err +} + // Start locks a goroutine to an OS thread and ties it to the namespace, then // loops for actions to run in the namespace. func (n *NamespaceGoroutine) Start() error {