From f7dd4f97c77af46fa4204ee20a340fa3fba2b4e0 Mon Sep 17 00:00:00 2001 From: Alexander Blagoev Date: Sat, 29 Apr 2017 18:57:54 +0300 Subject: [PATCH] Improve /proc/net/dev parsing to include all edge cases --- net/net_linux.go | 9 ++++-- net/net_linux_test.go | 67 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 2 deletions(-) diff --git a/net/net_linux.go b/net/net_linux.go index 58c0729..414f278 100644 --- a/net/net_linux.go +++ b/net/net_linux.go @@ -32,15 +32,20 @@ func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) { return nil, err } + parts := make([]string, 2) + statlen := len(lines) - 1 ret := make([]IOCountersStat, 0, statlen) for _, line := range lines[2:] { - parts := strings.SplitN(line, ": ", 2) - if len(parts) != 2 { + separatorPos := strings.LastIndex(line, ":") + if separatorPos == -1 { continue } + parts[0] = line[0:separatorPos] + parts[1] = line[separatorPos+1:] + interfaceName := strings.TrimSpace(parts[0]) if interfaceName == "" { continue diff --git a/net/net_linux_test.go b/net/net_linux_test.go index a881b7a..f87fa72 100644 --- a/net/net_linux_test.go +++ b/net/net_linux_test.go @@ -1,7 +1,10 @@ package net import ( + "fmt" + "io/ioutil" "os" + "strings" "syscall" "testing" @@ -9,6 +12,70 @@ import ( "github.com/stretchr/testify/assert" ) +func TestIOCountersByFileParsing(t *testing.T) { + // Prpare a temporary file, which will be read during the test + tmpfile, err := ioutil.TempFile("", "proc_dev_net") + defer os.Remove(tmpfile.Name()) // clean up + + assert.Nil(t, err, "Temporary file creation failed: ", err) + + cases := [4][2]string{ + [2]string{"eth0: ", "eth1: "}, + [2]string{"eth0:0: ", "eth1:0: "}, + [2]string{"eth0:", "eth1:"}, + [2]string{"eth0:0:", "eth1:0:"}, + } + for _, testCase := range cases { + err = tmpfile.Truncate(0) + assert.Nil(t, err, "Temporary file truncating problem: ", err) + + // Parse interface name for assertion + interface0 := strings.TrimSpace(testCase[0]) + interface0 = interface0[:len(interface0)-1] + + interface1 := strings.TrimSpace(testCase[1]) + interface1 = interface1[:len(interface1)-1] + + // Replace the interfaces from the test case + proc := []byte(fmt.Sprintf("Inter-| Receive | Transmit\n face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed\n %s1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16\n %s100 200 300 400 500 600 700 800 900 1000 1100 1200 1300 1400 1500 1600\n", testCase[0], testCase[1])) + + // Write /proc/net/dev sample output + _, err = tmpfile.Write(proc) + assert.Nil(t, err, "Temporary file writing failed: ", err) + + counters, err := IOCountersByFile(true, tmpfile.Name()) + + assert.Nil(t, err) + assert.NotEmpty(t, counters) + assert.Equal(t, 2, len(counters)) + assert.Equal(t, interface0, counters[0].Name) + assert.Equal(t, 1, int(counters[0].BytesRecv)) + assert.Equal(t, 2, int(counters[0].PacketsRecv)) + assert.Equal(t, 3, int(counters[0].Errin)) + assert.Equal(t, 4, int(counters[0].Dropin)) + assert.Equal(t, 5, int(counters[0].Fifoin)) + assert.Equal(t, 9, int(counters[0].BytesSent)) + assert.Equal(t, 10, int(counters[0].PacketsSent)) + assert.Equal(t, 11, int(counters[0].Errout)) + assert.Equal(t, 12, int(counters[0].Dropout)) + assert.Equal(t, 13, int(counters[0].Fifoout)) + assert.Equal(t, interface1, counters[1].Name) + assert.Equal(t, 100, int(counters[1].BytesRecv)) + assert.Equal(t, 200, int(counters[1].PacketsRecv)) + assert.Equal(t, 300, int(counters[1].Errin)) + assert.Equal(t, 400, int(counters[1].Dropin)) + assert.Equal(t, 500, int(counters[1].Fifoin)) + assert.Equal(t, 900, int(counters[1].BytesSent)) + assert.Equal(t, 1000, int(counters[1].PacketsSent)) + assert.Equal(t, 1100, int(counters[1].Errout)) + assert.Equal(t, 1200, int(counters[1].Dropout)) + assert.Equal(t, 1300, int(counters[1].Fifoout)) + } + + err = tmpfile.Close() + assert.Nil(t, err, "Temporary file closing failed: ", err) +} + func TestGetProcInodesAll(t *testing.T) { if os.Getenv("CIRCLECI") == "true" { t.Skip("Skip CI")