//go:build linux package ethtool import ( "errors" "net" "testing" "github.com/stretchr/testify/require" "github.com/influxdata/telegraf/testutil" ) var ( eth *Ethtool interfaceMap map[string]*interfaceMock ) type interfaceMock struct { name string driverName string namespaceName string stat map[string]uint64 loopBack bool interfaceUp bool cmdGet map[string]uint64 } type namespaceMock struct { namespaceName string } func (n *namespaceMock) name() string { return n.namespaceName } func (*namespaceMock) interfaces() ([]namespacedInterface, error) { return nil, errors.New("it is a test bug to invoke this function") } func (*namespaceMock) driverName(_ namespacedInterface) (string, error) { return "", errors.New("it is a test bug to invoke this function") } func (*namespaceMock) stats(_ namespacedInterface) (map[string]uint64, error) { return nil, errors.New("it is a test bug to invoke this function") } func (*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 } func (*commandEthtoolMock) init() error { // Not required for test mock return nil } func (c *commandEthtoolMock) driverName(intf namespacedInterface) (string, error) { i := c.interfaceMap[intf.Name] if i != nil { return i.driverName, nil } return "", errors.New("interface not found") } func (c *commandEthtoolMock) interfaces(includeNamespaces bool) ([]namespacedInterface, error) { namespaces := map[string]*namespaceMock{"": {namespaceName: ""}} interfaces := make([]namespacedInterface, 0) for k, v := range c.interfaceMap { if v.namespaceName != "" && !includeNamespaces { continue } var flag net.Flags // When interface is up if v.interfaceUp { flag |= net.FlagUp } // For loopback interface if v.loopBack { flag |= net.FlagLoopback } // Create a dummy interface iface := net.Interface{ Index: 0, MTU: 1500, Name: k, HardwareAddr: nil, Flags: flag, } // Ensure there is a namespace if necessary if _, ok := namespaces[v.namespaceName]; !ok { namespaces[v.namespaceName] = &namespaceMock{ namespaceName: v.namespaceName, } } interfaces = append( interfaces, namespacedInterface{ Interface: iface, namespace: namespaces[v.namespaceName], }, ) } return interfaces, nil } func (c *commandEthtoolMock) stats(intf namespacedInterface) (map[string]uint64, error) { i := c.interfaceMap[intf.Name] if i != nil { return i.stat, nil } 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) eth1Stat := map[string]uint64{ "interface_up": 1, "port_rx_1024_to_15xx": 25167245, "port_rx_128_to_255": 1573526387, "port_rx_15xx_to_jumbo": 137819058, "port_rx_256_to_511": 772038107, "port_rx_512_to_1023": 78294457, "port_rx_64": 8798065, "port_rx_65_to_127": 450348015, "port_rx_bad": 0, "port_rx_bad_bytes": 0, "port_rx_bad_gtjumbo": 0, "port_rx_broadcast": 6428250, "port_rx_bytes": 893460472634, "port_rx_control": 0, "port_rx_dp_di_dropped_packets": 2772680304, "port_rx_dp_hlb_fetch": 0, "port_rx_dp_hlb_wait": 0, "port_rx_dp_q_disabled_packets": 0, "port_rx_dp_streaming_packets": 0, "port_rx_good": 3045991334, "port_rx_good_bytes": 893460472927, "port_rx_gtjumbo": 0, "port_rx_lt64": 0, "port_rx_multicast": 1639566045, "port_rx_nodesc_drops": 0, "port_rx_overflow": 0, "port_rx_packets": 3045991334, "port_rx_pause": 0, "port_rx_pm_discard_bb_overflow": 0, "port_rx_pm_discard_mapping": 0, "port_rx_pm_discard_qbb": 0, "port_rx_pm_discard_vfifo_full": 0, "port_rx_pm_trunc_bb_overflow": 0, "port_rx_pm_trunc_qbb": 0, "port_rx_pm_trunc_vfifo_full": 0, "port_rx_unicast": 1399997040, "port_tx_1024_to_15xx": 236, "port_tx_128_to_255": 275090219, "port_tx_15xx_to_jumbo": 926, "port_tx_256_to_511": 48567221, "port_tx_512_to_1023": 5142016, "port_tx_64": 113903973, "port_tx_65_to_127": 161935699, "port_tx_broadcast": 8, "port_tx_bytes": 94357131016, "port_tx_control": 0, "port_tx_lt64": 0, "port_tx_multicast": 325891647, "port_tx_packets": 604640290, "port_tx_pause": 0, "port_tx_unicast": 278748635, "ptp_bad_syncs": 1, "ptp_fast_syncs": 1, "ptp_filter_matches": 0, "ptp_good_syncs": 136151, "ptp_invalid_sync_windows": 0, "ptp_no_time_syncs": 1, "ptp_non_filter_matches": 0, "ptp_oversize_sync_windows": 53, "ptp_rx_no_timestamp": 0, "ptp_rx_timestamp_packets": 0, "ptp_sync_timeouts": 1, "ptp_timestamp_packets": 0, "ptp_tx_timestamp_packets": 0, "ptp_undersize_sync_windows": 3, "rx-0.rx_packets": 55659234, "rx-1.rx_packets": 87880538, "rx-2.rx_packets": 26746234, "rx-3.rx_packets": 103026471, "rx-4.rx_packets": 0, "rx_eth_crc_err": 0, "rx_frm_trunc": 0, "rx_inner_ip_hdr_chksum_err": 0, "rx_inner_tcp_udp_chksum_err": 0, "rx_ip_hdr_chksum_err": 0, "rx_mcast_mismatch": 0, "rx_merge_events": 0, "rx_merge_packets": 0, "rx_nodesc_trunc": 0, "rx_noskb_drops": 0, "rx_outer_ip_hdr_chksum_err": 0, "rx_outer_tcp_udp_chksum_err": 0, "rx_reset": 0, "rx_tcp_udp_chksum_err": 0, "rx_tobe_disc": 0, "tx-0.tx_packets": 85843565, "tx-1.tx_packets": 108642725, "tx-2.tx_packets": 202596078, "tx-3.tx_packets": 207561010, "tx-4.tx_packets": 0, "tx_cb_packets": 4, "tx_merge_events": 11025, "tx_pio_packets": 531928114, "tx_pushes": 604643378, "tx_tso_bursts": 0, "tx_tso_fallbacks": 0, "tx_tso_long_headers": 0, } 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{ "interface_up": 0, "port_rx_1024_to_15xx": 11529312, "port_rx_128_to_255": 1868952037, "port_rx_15xx_to_jumbo": 130339387, "port_rx_256_to_511": 843846270, "port_rx_512_to_1023": 173194372, "port_rx_64": 9190374, "port_rx_65_to_127": 507806115, "port_rx_bad": 0, "port_rx_bad_bytes": 0, "port_rx_bad_gtjumbo": 0, "port_rx_broadcast": 6648019, "port_rx_bytes": 1007358162202, "port_rx_control": 0, "port_rx_dp_di_dropped_packets": 3164124639, "port_rx_dp_hlb_fetch": 0, "port_rx_dp_hlb_wait": 0, "port_rx_dp_q_disabled_packets": 0, "port_rx_dp_streaming_packets": 0, "port_rx_good": 3544857867, "port_rx_good_bytes": 1007358162202, "port_rx_gtjumbo": 0, "port_rx_lt64": 0, "port_rx_multicast": 2231999743, "port_rx_nodesc_drops": 0, "port_rx_overflow": 0, "port_rx_packets": 3544857867, "port_rx_pause": 0, "port_rx_pm_discard_bb_overflow": 0, "port_rx_pm_discard_mapping": 0, "port_rx_pm_discard_qbb": 0, "port_rx_pm_discard_vfifo_full": 0, "port_rx_pm_trunc_bb_overflow": 0, "port_rx_pm_trunc_qbb": 0, "port_rx_pm_trunc_vfifo_full": 0, "port_rx_unicast": 1306210105, "port_tx_1024_to_15xx": 379, "port_tx_128_to_255": 202767251, "port_tx_15xx_to_jumbo": 558, "port_tx_256_to_511": 31454719, "port_tx_512_to_1023": 6865731, "port_tx_64": 17268276, "port_tx_65_to_127": 272816313, "port_tx_broadcast": 6, "port_tx_bytes": 78071946593, "port_tx_control": 0, "port_tx_lt64": 0, "port_tx_multicast": 239510586, "port_tx_packets": 531173227, "port_tx_pause": 0, "port_tx_unicast": 291662635, "ptp_bad_syncs": 0, "ptp_fast_syncs": 0, "ptp_filter_matches": 0, "ptp_good_syncs": 0, "ptp_invalid_sync_windows": 0, "ptp_no_time_syncs": 0, "ptp_non_filter_matches": 0, "ptp_oversize_sync_windows": 0, "ptp_rx_no_timestamp": 0, "ptp_rx_timestamp_packets": 0, "ptp_sync_timeouts": 0, "ptp_timestamp_packets": 0, "ptp_tx_timestamp_packets": 0, "ptp_undersize_sync_windows": 0, "rx-0.rx_packets": 84587075, "rx-1.rx_packets": 74029305, "rx-2.rx_packets": 134586471, "rx-3.rx_packets": 87531322, "rx-4.rx_packets": 0, "rx_eth_crc_err": 0, "rx_frm_trunc": 0, "rx_inner_ip_hdr_chksum_err": 0, "rx_inner_tcp_udp_chksum_err": 0, "rx_ip_hdr_chksum_err": 0, "rx_mcast_mismatch": 0, "rx_merge_events": 0, "rx_merge_packets": 0, "rx_nodesc_trunc": 0, "rx_noskb_drops": 0, "rx_outer_ip_hdr_chksum_err": 0, "rx_outer_tcp_udp_chksum_err": 0, "rx_reset": 0, "rx_tcp_udp_chksum_err": 0, "rx_tobe_disc": 0, "tx-0.tx_packets": 232521451, "tx-1.tx_packets": 97876137, "tx-2.tx_packets": 106822111, "tx-3.tx_packets": 93955050, "tx-4.tx_packets": 0, "tx_cb_packets": 1, "tx_merge_events": 8402, "tx_pio_packets": 481040054, "tx_pushes": 531174491, "tx_tso_bursts": 128, "tx_tso_fallbacks": 0, "tx_tso_long_headers": 0, } 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{ "interface_up": 1, "port_rx_1024_to_15xx": 25167245, "port_rx_128_to_255": 1573526387, "port_rx_15xx_to_jumbo": 137819058, "port_rx_256_to_511": 772038107, "port_rx_512_to_1023": 78294457, "port_rx_64": 8798065, "port_rx_65_to_127": 450348015, "port_rx_bad": 0, "port_rx_bad_bytes": 0, "port_rx_bad_gtjumbo": 0, "port_rx_broadcast": 6428250, "port_rx_bytes": 893460472634, "port_rx_control": 0, "port_rx_dp_di_dropped_packets": 2772680304, "port_rx_dp_hlb_fetch": 0, "port_rx_dp_hlb_wait": 0, "port_rx_dp_q_disabled_packets": 0, "port_rx_dp_streaming_packets": 0, "port_rx_good": 3045991334, "port_rx_good_bytes": 893460472927, "port_rx_gtjumbo": 0, "port_rx_lt64": 0, "port_rx_multicast": 1639566045, "port_rx_nodesc_drops": 0, "port_rx_overflow": 0, "port_rx_packets": 3045991334, "port_rx_pause": 0, "port_rx_pm_discard_bb_overflow": 0, "port_rx_pm_discard_mapping": 0, "port_rx_pm_discard_qbb": 0, "port_rx_pm_discard_vfifo_full": 0, "port_rx_pm_trunc_bb_overflow": 0, "port_rx_pm_trunc_qbb": 0, "port_rx_pm_trunc_vfifo_full": 0, "port_rx_unicast": 1399997040, "port_tx_1024_to_15xx": 236, "port_tx_128_to_255": 275090219, "port_tx_15xx_to_jumbo": 926, "port_tx_256_to_511": 48567221, "port_tx_512_to_1023": 5142016, "port_tx_64": 113903973, "port_tx_65_to_127": 161935699, "port_tx_broadcast": 8, "port_tx_bytes": 94357131016, "port_tx_control": 0, "port_tx_lt64": 0, "port_tx_multicast": 325891647, "port_tx_packets": 604640290, "port_tx_pause": 0, "port_tx_unicast": 278748635, "ptp_bad_syncs": 1, "ptp_fast_syncs": 1, "ptp_filter_matches": 0, "ptp_good_syncs": 136151, "ptp_invalid_sync_windows": 0, "ptp_no_time_syncs": 1, "ptp_non_filter_matches": 0, "ptp_oversize_sync_windows": 53, "ptp_rx_no_timestamp": 0, "ptp_rx_timestamp_packets": 0, "ptp_sync_timeouts": 1, "ptp_timestamp_packets": 0, "ptp_tx_timestamp_packets": 0, "ptp_undersize_sync_windows": 3, "rx-0.rx_packets": 55659234, "rx-1.rx_packets": 87880538, "rx-2.rx_packets": 26746234, "rx-3.rx_packets": 103026471, "rx-4.rx_packets": 0, "rx_eth_crc_err": 0, "rx_frm_trunc": 0, "rx_inner_ip_hdr_chksum_err": 0, "rx_inner_tcp_udp_chksum_err": 0, "rx_ip_hdr_chksum_err": 0, "rx_mcast_mismatch": 0, "rx_merge_events": 0, "rx_merge_packets": 0, "rx_nodesc_trunc": 0, "rx_noskb_drops": 0, "rx_outer_ip_hdr_chksum_err": 0, "rx_outer_tcp_udp_chksum_err": 0, "rx_reset": 0, "rx_tcp_udp_chksum_err": 0, "rx_tobe_disc": 0, "tx-0.tx_packets": 85843565, "tx-1.tx_packets": 108642725, "tx-2.tx_packets": 202596078, "tx-3.tx_packets": 207561010, "tx-4.tx_packets": 0, "tx_cb_packets": 4, "tx_merge_events": 11025, "tx_pio_packets": 531928114, "tx_pushes": 604643378, "tx_tso_bursts": 0, "tx_tso_fallbacks": 0, "tx_tso_long_headers": 0, } 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{ "interface_up": 1, "port_rx_1024_to_15xx": 25167245, "port_rx_128_to_255": 1573526387, "port_rx_15xx_to_jumbo": 137819058, "port_rx_256_to_511": 772038107, "port_rx_512_to_1023": 78294457, "port_rx_64": 8798065, "port_rx_65_to_127": 450348015, "port_rx_bad": 0, "port_rx_bad_bytes": 0, "port_rx_bad_gtjumbo": 0, "port_rx_broadcast": 6428250, "port_rx_bytes": 893460472634, "port_rx_control": 0, "port_rx_dp_di_dropped_packets": 2772680304, "port_rx_dp_hlb_fetch": 0, "port_rx_dp_hlb_wait": 0, "port_rx_dp_q_disabled_packets": 0, "port_rx_dp_streaming_packets": 0, "port_rx_good": 3045991334, "port_rx_good_bytes": 893460472927, "port_rx_gtjumbo": 0, "port_rx_lt64": 0, "port_rx_multicast": 1639566045, "port_rx_nodesc_drops": 0, "port_rx_overflow": 0, "port_rx_packets": 3045991334, "port_rx_pause": 0, "port_rx_pm_discard_bb_overflow": 0, "port_rx_pm_discard_mapping": 0, "port_rx_pm_discard_qbb": 0, "port_rx_pm_discard_vfifo_full": 0, "port_rx_pm_trunc_bb_overflow": 0, "port_rx_pm_trunc_qbb": 0, "port_rx_pm_trunc_vfifo_full": 0, "port_rx_unicast": 1399997040, "port_tx_1024_to_15xx": 236, "port_tx_128_to_255": 275090219, "port_tx_15xx_to_jumbo": 926, "port_tx_256_to_511": 48567221, "port_tx_512_to_1023": 5142016, "port_tx_64": 113903973, "port_tx_65_to_127": 161935699, "port_tx_broadcast": 8, "port_tx_bytes": 94357131016, "port_tx_control": 0, "port_tx_lt64": 0, "port_tx_multicast": 325891647, "port_tx_packets": 604640290, "port_tx_pause": 0, "port_tx_unicast": 278748635, "ptp_bad_syncs": 1, "ptp_fast_syncs": 1, "ptp_filter_matches": 0, "ptp_good_syncs": 136151, "ptp_invalid_sync_windows": 0, "ptp_no_time_syncs": 1, "ptp_non_filter_matches": 0, "ptp_oversize_sync_windows": 53, "ptp_rx_no_timestamp": 0, "ptp_rx_timestamp_packets": 0, "ptp_sync_timeouts": 1, "ptp_timestamp_packets": 0, "ptp_tx_timestamp_packets": 0, "ptp_undersize_sync_windows": 3, "rx-0.rx_packets": 55659234, "rx-1.rx_packets": 87880538, "rx-2.rx_packets": 26746234, "rx-3.rx_packets": 103026471, "rx-4.rx_packets": 0, "rx_eth_crc_err": 0, "rx_frm_trunc": 0, "rx_inner_ip_hdr_chksum_err": 0, "rx_inner_tcp_udp_chksum_err": 0, "rx_ip_hdr_chksum_err": 0, "rx_mcast_mismatch": 0, "rx_merge_events": 0, "rx_merge_packets": 0, "rx_nodesc_trunc": 0, "rx_noskb_drops": 0, "rx_outer_ip_hdr_chksum_err": 0, "rx_outer_tcp_udp_chksum_err": 0, "rx_reset": 0, "rx_tcp_udp_chksum_err": 0, "rx_tobe_disc": 0, "tx-0.tx_packets": 85843565, "tx-1.tx_packets": 108642725, "tx-2.tx_packets": 202596078, "tx-3.tx_packets": 207561010, "tx-4.tx_packets": 0, "tx_cb_packets": 4, "tx_merge_events": 11025, "tx_pio_packets": 531928114, "tx_pushes": 604643378, "tx_tso_bursts": 0, "tx_tso_fallbacks": 0, "tx_tso_long_headers": 0, } 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, } 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} eth = &Ethtool{ DownInterfaces: "expose", command: c, } } func toStringMapInterface(in map[string]uint64) map[string]interface{} { m := map[string]interface{}{} for k, v := range in { m[k] = v } return m } func toStringMapUint(in map[string]interface{}) map[string]uint64 { m := map[string]uint64{} for k, v := range in { t := v.(uint64) m[k] = t } return m } func TestGather(t *testing.T) { setup() err := eth.Init() require.NoError(t, err) var acc testutil.Accumulator err = eth.Gather(&acc) require.NoError(t, err) 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 expectedTagsEth1 := map[string]string{ "interface": "eth1", "driver": "driver1", "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{ "driver": "driver1", "interface": "eth2", "namespace": "", } acc.AssertContainsTaggedFields(t, pluginName, expectedFieldsEth2, expectedTagsEth2) } func TestGatherIncludeInterfaces(t *testing.T) { setup() eth.InterfaceInclude = append(eth.InterfaceInclude, "eth1") err := eth.Init() require.NoError(t, err) var acc testutil.Accumulator err = eth.Gather(&acc) require.NoError(t, err) require.Len(t, acc.Metrics, 1) // 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{ "interface": "eth1", "driver": "driver1", "namespace": "", } acc.AssertContainsTaggedFields(t, pluginName, expectedFieldsEth1, expectedTagsEth1) // 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{ "interface": "eth2", "driver": "driver1", "namespace": "", } acc.AssertDoesNotContainsTaggedFields(t, pluginName, expectedFieldsEth2, expectedTagsEth2) } func TestGatherIgnoreInterfaces(t *testing.T) { setup() eth.InterfaceExclude = append(eth.InterfaceExclude, "eth1") err := eth.Init() require.NoError(t, err) var acc testutil.Accumulator err = eth.Gather(&acc) require.NoError(t, err) require.Len(t, acc.Metrics, 1) // 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{ "interface": "eth1", "driver": "driver1", "namespace": "", } acc.AssertDoesNotContainsTaggedFields(t, pluginName, expectedFieldsEth1, expectedTagsEth1) // 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{ "interface": "eth2", "driver": "driver1", "namespace": "", } acc.AssertContainsTaggedFields(t, pluginName, expectedFieldsEth2, expectedTagsEth2) } func TestSkipMetricsForInterfaceDown(t *testing.T) { setup() eth.DownInterfaces = "skip" err := eth.Init() require.NoError(t, err) var acc testutil.Accumulator err = eth.Gather(&acc) require.NoError(t, err) 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 expectedTagsEth1 := map[string]string{ "interface": "eth1", "driver": "driver1", "namespace": "", } acc.AssertContainsTaggedFields(t, pluginName, expectedFieldsEth1, expectedTagsEth1) } func TestGatherIncludeNamespaces(t *testing.T) { setup() var acc testutil.Accumulator eth.NamespaceInclude = append(eth.NamespaceInclude, "namespace1") err := eth.Init() require.NoError(t, err) err = eth.Gather(&acc) require.NoError(t, err) require.Len(t, acc.Metrics, 1) // 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{ "interface": "eth3", "driver": "driver1", "namespace": "namespace1", } acc.AssertContainsTaggedFields(t, pluginName, expectedFieldsEth3, expectedTagsEth3) // 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{ "interface": "eth2", "driver": "driver1", "namespace": "", } acc.AssertDoesNotContainsTaggedFields(t, pluginName, expectedFieldsEth2, expectedTagsEth2) } func TestGatherIgnoreNamespaces(t *testing.T) { setup() var acc testutil.Accumulator eth.NamespaceExclude = append(eth.NamespaceExclude, "namespace2") err := eth.Init() require.NoError(t, err) err = eth.Gather(&acc) require.NoError(t, err) require.Len(t, acc.Metrics, 3) // 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{ "interface": "eth4", "driver": "driver1", "namespace": "namespace2", } acc.AssertDoesNotContainsTaggedFields(t, pluginName, expectedFieldsEth4, expectedTagsEth4) // 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{ "interface": "eth2", "driver": "driver1", "namespace": "", } acc.AssertContainsTaggedFields(t, pluginName, expectedFieldsEth2, expectedTagsEth2) // 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{ "interface": "eth3", "driver": "driver1", "namespace": "namespace1", } acc.AssertContainsTaggedFields(t, pluginName, expectedFieldsEth3, expectedTagsEth3) } type testCase struct { normalization []string stats map[string]interface{} expectedFields map[string]interface{} } func TestNormalizedKeys(t *testing.T) { cases := []testCase{ { normalization: []string{"underscore"}, stats: map[string]interface{}{ "port rx": uint64(1), " Port_tx": uint64(0), "interface_up": uint64(0), }, expectedFields: map[string]interface{}{ "port_rx": uint64(1), "_Port_tx": uint64(0), "interface_up": true, "interface_up_counter": uint64(0), }, }, { normalization: []string{"underscore", "lower"}, stats: map[string]interface{}{ "Port rx": uint64(1), " Port_tx": uint64(0), "interface_up": uint64(0), }, expectedFields: map[string]interface{}{ "port_rx": uint64(1), "_port_tx": uint64(0), "interface_up": true, "interface_up_counter": uint64(0), }, }, { normalization: []string{"underscore", "lower", "trim"}, stats: map[string]interface{}{ " Port RX ": uint64(1), " Port_tx": uint64(0), "interface_up": uint64(0), }, expectedFields: map[string]interface{}{ "port_rx": uint64(1), "port_tx": uint64(0), "interface_up": true, "interface_up_counter": uint64(0), }, }, { normalization: []string{"underscore", "lower", "snakecase", "trim"}, stats: map[string]interface{}{ " Port RX ": uint64(1), " Port_tx": uint64(0), "interface_up": uint64(0), }, expectedFields: map[string]interface{}{ "port_rx": uint64(1), "port_tx": uint64(0), "interface_up": true, "interface_up_counter": uint64(0), }, }, { normalization: []string{"snakecase"}, stats: map[string]interface{}{ " PortRX ": uint64(1), " PortTX": uint64(0), "interface_up": uint64(0), }, expectedFields: map[string]interface{}{ "port_rx": uint64(1), "port_tx": uint64(0), "interface_up": true, "interface_up_counter": uint64(0), }, }, { stats: map[string]interface{}{ " Port RX ": uint64(1), " Port_tx": uint64(0), "interface_up": uint64(0), }, expectedFields: map[string]interface{}{ " Port RX ": uint64(1), " Port_tx": uint64(0), "interface_up": true, "interface_up_counter": uint64(0), }, }, { stats: map[string]interface{}{ " Port RX ": uint64(1), " Port_tx": uint64(0), }, expectedFields: map[string]interface{}{ " Port RX ": uint64(1), " Port_tx": uint64(0), "interface_up": true, }, }, } for _, c := range cases { eth0 := &interfaceMock{"eth0", "e1000e", "", toStringMapUint(c.stats), false, true, map[string]uint64{}} expectedTags := map[string]string{ "interface": eth0.name, "driver": eth0.driverName, "namespace": "", } interfaceMap = make(map[string]*interfaceMock) interfaceMap[eth0.name] = eth0 cmd := &commandEthtoolMock{interfaceMap} eth = &Ethtool{ NormalizeKeys: c.normalization, command: cmd, } err := eth.Init() require.NoError(t, err) var acc testutil.Accumulator err = eth.Gather(&acc) require.NoError(t, err) require.Len(t, acc.Metrics, 1) acc.AssertContainsFields(t, pluginName, c.expectedFields) acc.AssertContainsTaggedFields(t, pluginName, c.expectedFields, expectedTags) } }