From 1e3597d8894c3b0f9cd9f97ca7c81052f7bc17e2 Mon Sep 17 00:00:00 2001 From: Dane Strandboge <136023093+DStrand1@users.noreply.github.com> Date: Mon, 29 Jan 2024 14:11:18 -0600 Subject: [PATCH] feat(inputs.net): Add speed metric (#14625) --- plugins/inputs/net/README.md | 1 + plugins/inputs/net/net.go | 23 +++ plugins/inputs/net/net_test.go | 131 ++++++++++++++++++ .../testdata/general/sys/class/net/eth0/speed | 1 + .../testdata/general/sys/class/net/eth1/speed | 1 + 5 files changed, 157 insertions(+) create mode 100644 plugins/inputs/net/testdata/general/sys/class/net/eth0/speed create mode 100644 plugins/inputs/net/testdata/general/sys/class/net/eth1/speed diff --git a/plugins/inputs/net/README.md b/plugins/inputs/net/README.md index 1309584d7..9a5eb605f 100644 --- a/plugins/inputs/net/README.md +++ b/plugins/inputs/net/README.md @@ -47,6 +47,7 @@ Fields (all platforms): * err_out - The total number of transmit errors detected by the interface * drop_in - The total number of received packets dropped by the interface * drop_out - The total number of transmitted packets dropped by the interface +* speed - The interface's latest or current speed value, in Mbits/sec. May be -1 if unsupported by the interface Different platforms gather the data above with different mechanisms. Telegraf uses the ([gopsutil](https://github.com/shirou/gopsutil)) package, which under diff --git a/plugins/inputs/net/net.go b/plugins/inputs/net/net.go index 78dc7d63a..ca32078b1 100644 --- a/plugins/inputs/net/net.go +++ b/plugins/inputs/net/net.go @@ -5,6 +5,9 @@ import ( _ "embed" "fmt" "net" + "os" + "path/filepath" + "strconv" "strings" "github.com/influxdata/telegraf" @@ -104,6 +107,7 @@ func (n *NetIOStats) Gather(acc telegraf.Accumulator) error { "err_out": io.Errout, "drop_in": io.Dropin, "drop_out": io.Dropout, + "speed": getInterfaceSpeed(io.Name), } acc.AddCounter("net", fields, tags) } @@ -129,6 +133,25 @@ func (n *NetIOStats) Gather(acc telegraf.Accumulator) error { return nil } +// Get the interface speed from /sys/class/net/*/speed file. returns -1 if unsupported +func getInterfaceSpeed(ioName string) int64 { + sysPath := os.Getenv("HOST_SYS") + if sysPath == "" { + sysPath = "/sys" + } + + raw, err := os.ReadFile(filepath.Join(sysPath, "class", "net", ioName, "speed")) + if err != nil { + return -1 + } + + speed, err := strconv.ParseInt(strings.TrimSuffix(string(raw), "\n"), 10, 64) + if err != nil { + return -1 + } + return speed +} + func init() { inputs.Add("net", func() telegraf.Input { return &NetIOStats{ps: system.NewSystemPS()} diff --git a/plugins/inputs/net/net_test.go b/plugins/inputs/net/net_test.go index e497c24a7..19b60f480 100644 --- a/plugins/inputs/net/net_test.go +++ b/plugins/inputs/net/net_test.go @@ -1,6 +1,8 @@ package net import ( + "os" + "path/filepath" "testing" "github.com/influxdata/telegraf/plugins/inputs/system" @@ -40,6 +42,8 @@ func TestNetIOStats(t *testing.T) { } mps.On("NetProto").Return(netprotos, nil) + require.NoError(t, os.Setenv("HOST_SYS", filepath.Join("testdata", "general", "sys"))) + err = (&NetIOStats{ps: &mps, skipChecks: true}).Gather(&acc) require.NoError(t, err) @@ -56,6 +60,133 @@ func TestNetIOStats(t *testing.T) { "err_out": uint64(8), "drop_in": uint64(7), "drop_out": uint64(1), + "speed": int64(100), + } + acc.AssertContainsTaggedFields(t, "net", fields1, ntags) + + fields2 := map[string]interface{}{ + "udp_noports": int64(892592), + "udp_indatagrams": int64(4655), + } + ntags = map[string]string{ + "interface": "all", + } + acc.AssertContainsTaggedFields(t, "net", fields2, ntags) +} + +func TestNetIOStatsSpeedUnsupported(t *testing.T) { + var mps system.MockPS + var err error + defer mps.AssertExpectations(t) + var acc testutil.Accumulator + + netio := net.IOCountersStat{ + Name: "eth1", + BytesSent: 1123, + BytesRecv: 8734422, + PacketsSent: 781, + PacketsRecv: 23456, + Errin: 832, + Errout: 8, + Dropin: 7, + Dropout: 1, + } + + mps.On("NetIO").Return([]net.IOCountersStat{netio}, nil) + + netprotos := []net.ProtoCountersStat{ + { + Protocol: "Udp", + Stats: map[string]int64{ + "InDatagrams": 4655, + "NoPorts": 892592, + }, + }, + } + mps.On("NetProto").Return(netprotos, nil) + + require.NoError(t, os.Setenv("HOST_SYS", filepath.Join("testdata", "general", "sys"))) + + err = (&NetIOStats{ps: &mps, skipChecks: true}).Gather(&acc) + require.NoError(t, err) + + ntags := map[string]string{ + "interface": "eth1", + } + + fields1 := map[string]interface{}{ + "bytes_sent": uint64(1123), + "bytes_recv": uint64(8734422), + "packets_sent": uint64(781), + "packets_recv": uint64(23456), + "err_in": uint64(832), + "err_out": uint64(8), + "drop_in": uint64(7), + "drop_out": uint64(1), + "speed": int64(-1), + } + acc.AssertContainsTaggedFields(t, "net", fields1, ntags) + + fields2 := map[string]interface{}{ + "udp_noports": int64(892592), + "udp_indatagrams": int64(4655), + } + ntags = map[string]string{ + "interface": "all", + } + acc.AssertContainsTaggedFields(t, "net", fields2, ntags) +} + +func TestNetIOStatsNoSpeedFile(t *testing.T) { + var mps system.MockPS + var err error + defer mps.AssertExpectations(t) + var acc testutil.Accumulator + + netio := net.IOCountersStat{ + Name: "eth2", + BytesSent: 1123, + BytesRecv: 8734422, + PacketsSent: 781, + PacketsRecv: 23456, + Errin: 832, + Errout: 8, + Dropin: 7, + Dropout: 1, + } + + mps.On("NetIO").Return([]net.IOCountersStat{netio}, nil) + + netprotos := []net.ProtoCountersStat{ + { + Protocol: "Udp", + Stats: map[string]int64{ + "InDatagrams": 4655, + "NoPorts": 892592, + }, + }, + } + mps.On("NetProto").Return(netprotos, nil) + + require.NoError(t, os.Setenv("HOST_SYS", filepath.Join("testdata", "general", "sys"))) + + err = (&NetIOStats{ps: &mps, skipChecks: true}).Gather(&acc) + require.NoError(t, err) + + ntags := map[string]string{ + "interface": "eth2", + } + + fields1 := map[string]interface{}{ + "bytes_sent": uint64(1123), + "bytes_recv": uint64(8734422), + "packets_sent": uint64(781), + "packets_recv": uint64(23456), + "err_in": uint64(832), + "err_out": uint64(8), + "drop_in": uint64(7), + "drop_out": uint64(1), + "speed": int64(-1), } acc.AssertContainsTaggedFields(t, "net", fields1, ntags) diff --git a/plugins/inputs/net/testdata/general/sys/class/net/eth0/speed b/plugins/inputs/net/testdata/general/sys/class/net/eth0/speed new file mode 100644 index 000000000..29d6383b5 --- /dev/null +++ b/plugins/inputs/net/testdata/general/sys/class/net/eth0/speed @@ -0,0 +1 @@ +100 diff --git a/plugins/inputs/net/testdata/general/sys/class/net/eth1/speed b/plugins/inputs/net/testdata/general/sys/class/net/eth1/speed new file mode 100644 index 000000000..3a2e3f498 --- /dev/null +++ b/plugins/inputs/net/testdata/general/sys/class/net/eth1/speed @@ -0,0 +1 @@ +-1