diff --git a/net/net.go b/net/net.go index 0f3a62f39..36145c8bb 100644 --- a/net/net.go +++ b/net/net.go @@ -11,17 +11,19 @@ import ( var invoke common.Invoker = common.Invoke{} type IOCountersStat struct { - Name string `json:"name"` // interface name - BytesSent uint64 `json:"bytesSent"` // number of bytes sent - BytesRecv uint64 `json:"bytesRecv"` // number of bytes received - PacketsSent uint64 `json:"packetsSent"` // number of packets sent - PacketsRecv uint64 `json:"packetsRecv"` // number of packets received - Errin uint64 `json:"errin"` // total number of errors while receiving - Errout uint64 `json:"errout"` // total number of errors while sending - Dropin uint64 `json:"dropin"` // total number of incoming packets which were dropped - Dropout uint64 `json:"dropout"` // total number of outgoing packets which were dropped (always 0 on OSX and BSD) - Fifoin uint64 `json:"fifoin"` // total number of FIFO buffers errors while receiving - Fifoout uint64 `json:"fifoout"` // total number of FIFO buffers errors while sending + Name string `json:"name"` // interface name + BytesSent uint64 `json:"bytesSent"` // number of bytes sent + BytesRecv uint64 `json:"bytesRecv"` // number of bytes received + PacketsSent uint64 `json:"packetsSent"` // number of packets sent + PacketsRecv uint64 `json:"packetsRecv"` // number of packets received + Errin uint64 `json:"errin"` // total number of errors while receiving + Errout uint64 `json:"errout"` // total number of errors while sending + Dropin uint64 `json:"dropin"` // total number of incoming packets which were dropped + Dropout uint64 `json:"dropout"` // total number of outgoing packets which were dropped (always 0 on OSX and BSD) + Fifoin uint64 `json:"fifoin"` // total number of FIFO buffers errors while receiving + Fifoout uint64 `json:"fifoout"` // total number of FIFO buffers errors while sending + TransmitSpeed uint64 `json:"transmitSpeed"` // transmission speed of interface + ReceiveSpeed uint64 `json:"receiveSpeed"` // receiving speed of interface } // Addr is implemented compatibility to psutil diff --git a/net/net_darwin.go b/net/net_darwin.go index 4d2cfbcd8..fac6187a9 100644 --- a/net/net_darwin.go +++ b/net/net_darwin.go @@ -124,6 +124,44 @@ func parseNetstatOutput(output string) ([]netstatInterface, error) { return interfaces, nil } +func parseIfconfigBlock(block string) uint64 { + + re := regexp.MustCompile(`media:\s+.*\((\d+)baseT`) + match := re.FindStringSubmatch(block) + + if len(match) >= 2 { + speed, err := strconv.ParseUint(match[1], 10, 64) + if err != nil { + speed = 0 + } + + return speed + } + + return 0 + +} + +func parseIfconfigOutput(output string, stats []IOCountersStat) error { + + re := regexp.MustCompile(`(?m)^(\w+):.*(?:\n\t.+)*`) + + matches := re.FindAllStringSubmatch(output, -1) + ifconfigBlock := map[string]string{} + + for _, match := range matches { + ifconfigBlock[match[1]] = match[0] + } + + for i, _ := range stats { + speed := parseIfconfigBlock(ifconfigBlock[stats[i].Name]) + stats[i].TransmitSpeed = speed + stats[i].ReceiveSpeed = speed + } + + return nil +} + // map that hold the name of a network interface and the number of usage type mapInterfaceNameUsage map[string]uint @@ -247,6 +285,15 @@ func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, } } + out, err = invoke.CommandWithContext(ctx, "ifconfig", "-a") + if err != nil { + return nil, err + } + err = parseIfconfigOutput(string(out), ret) + if err != nil { + return nil, err + } + if pernic == false { return getIOCountersAll(ret) } diff --git a/net/net_darwin_test.go b/net/net_darwin_test.go index 0680d08ad..f89952689 100644 --- a/net/net_darwin_test.go +++ b/net/net_darwin_test.go @@ -28,6 +28,27 @@ gif0* 1280 0 0 0 0 stf0* 1280 0 0 0 0 0 0 0 0 en0 1500 a8:66:7f:dd:ee:ff 5708989 0 7295722068 3494252 0 379533492 0 230 en0 1500 fe80::aa66: fe80:4::aa66:7fff 5708989 - 7295722068 3494252 - 379533492 - -` + ifconfigOutput = `lo0: flags=8049 mtu 16384 + options=1203 + inet 192.168.0.100 netmask 255.255.255.0 + inet6 fe80::1234:5678:abcd:ef01%lo0 prefixlen 64 scopeid 0x1 + inet6 fe80::5678:abcd:ef01:1234%lo0 prefixlen 64 scopeid 0x1 + nd6 options=201 +gif0: flags=8010 mtu 1280 +stf0: flags=0<> mtu 1280 +en0: flags=8863 mtu 1500 + ether 11:22:33:44:55:66 + inet6 fe80::abcd:1234:5678:ef01%en0 prefixlen 64 secured scopeid 0x4 + inet 192.168.1.100 netmask 255.255.255.0 broadcast 192.168.1.255 + nd6 options=201 + media: autoselect (1000baseT ) + status: active +utun0: flags=8051 mtu 1380 + inet6 fe80::1234:5678:abcd:ef01%utun0 prefixlen 64 scopeid 0x5 + nd6 options=201 +utun1: flags=8051 mtu 2000 + inet6 fe80::abcd:5678:ef01:1234%utun1 prefixlen 64 scopeid 0x6 + nd6 options=201` ) func TestParseNetstatLineHeader(t *testing.T) { @@ -138,3 +159,23 @@ func TestParseNetstatTruncated(t *testing.T) { assert.True(t, mapUsage.isTruncated()) assert.Equal(t, 3, len(mapUsage.notTruncated()), "en0, gif0 and stf0") } + +func TestParseIfconfigOutput(t *testing.T) { + testStats := []IOCountersStat{ + {Name: "lo0"}, + {Name: "en0"}, + } + err := parseIfconfigOutput(ifconfigOutput, testStats) + assert.NoError(t, err) + assert.Len(t, testStats, 2) + + assert.NotNil(t, testStats[0].TransmitSpeed) + assert.Equal(t, uint64(0), testStats[0].TransmitSpeed) + assert.NotNil(t, testStats[0].ReceiveSpeed) + assert.Equal(t, uint64(0), testStats[0].ReceiveSpeed) + + assert.NotNil(t, testStats[1].TransmitSpeed) + assert.Equal(t, uint64(1000), testStats[1].TransmitSpeed) + assert.NotNil(t, testStats[1].ReceiveSpeed) + assert.Equal(t, uint64(1000), testStats[1].ReceiveSpeed) +} diff --git a/net/net_freebsd.go b/net/net_freebsd.go index bf8baf094..93b5193f6 100644 --- a/net/net_freebsd.go +++ b/net/net_freebsd.go @@ -5,6 +5,8 @@ package net import ( "context" + "os/exec" + "regexp" "strconv" "strings" @@ -80,6 +82,10 @@ func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, BytesSent: parsed[6], Dropout: parsed[7], } + err := parseIfconfigOutput(&n) + if err != nil { + return nil, err + } ret = append(ret, n) } @@ -90,6 +96,27 @@ func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, return ret, nil } +func parseIfconfigOutput(istat *IOCountersStat) error { + cmd := exec.Command("ifconfig", istat.Name) + out, err := cmd.Output() + if err != nil { + return err + } + + re := regexp.MustCompile(`media:\s+.*\((\d+)baseT`) + match := re.FindStringSubmatch(string(out)) + if len(match) >= 2 { + speed, err := strconv.ParseUint(match[1], 10, 64) + if err != nil { + speed = 0 + } + istat.TransmitSpeed = speed + istat.ReceiveSpeed = speed + } + + return nil +} + // IOCountersByFile exists just for compatibility with Linux. func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) { return IOCountersByFileWithContext(context.Background(), pernic, filename) diff --git a/net/net_linux.go b/net/net_linux.go index dd62d4791..8228c4401 100644 --- a/net/net_linux.go +++ b/net/net_linux.go @@ -83,6 +83,18 @@ func IOCountersByFileWithContext(ctx context.Context, pernic bool, filename stri continue } + speed, err := common.ReadLines(common.HostSys(fmt.Sprintf("class/net/%s/speed", interfaceName))) + if err != nil { + if strings.Contains(err.Error(), "no such file or directory") { + speed = []string{"0"} + } else { + return ret, err + } + } + if len(speed) == 0 || speed[0] == "-1" { + speed = []string{"0"} + } + fields := strings.Fields(strings.TrimSpace(parts[1])) bytesRecv, err := strconv.ParseUint(fields[0], 10, 64) if err != nil { @@ -124,19 +136,29 @@ func IOCountersByFileWithContext(ctx context.Context, pernic bool, filename stri if err != nil { return ret, err } + transmitSpeed, err := strconv.ParseUint(speed[0], 10, 64) + if err != nil { + return ret, err + } + receiveSpeed, err := strconv.ParseUint(speed[0], 10, 64) + if err != nil { + return ret, err + } nic := IOCountersStat{ - Name: interfaceName, - BytesRecv: bytesRecv, - PacketsRecv: packetsRecv, - Errin: errIn, - Dropin: dropIn, - Fifoin: fifoIn, - BytesSent: bytesSent, - PacketsSent: packetsSent, - Errout: errOut, - Dropout: dropOut, - Fifoout: fifoOut, + Name: interfaceName, + BytesRecv: bytesRecv, + PacketsRecv: packetsRecv, + Errin: errIn, + Dropin: dropIn, + Fifoin: fifoIn, + BytesSent: bytesSent, + PacketsSent: packetsSent, + Errout: errOut, + Dropout: dropOut, + Fifoout: fifoOut, + TransmitSpeed: transmitSpeed, + ReceiveSpeed: receiveSpeed, } ret = append(ret, nic) } diff --git a/net/net_test.go b/net/net_test.go index 72f4db9c5..4951dbb83 100644 --- a/net/net_test.go +++ b/net/net_test.go @@ -30,7 +30,7 @@ func TestNetIOCountersStatString(t *testing.T) { Name: "test", BytesSent: 100, } - e := `{"name":"test","bytesSent":100,"bytesRecv":0,"packetsSent":0,"packetsRecv":0,"errin":0,"errout":0,"dropin":0,"dropout":0,"fifoin":0,"fifoout":0}` + e := `{"name":"test","bytesSent":100,"bytesRecv":0,"packetsSent":0,"packetsRecv":0,"errin":0,"errout":0,"dropin":0,"dropout":0,"fifoin":0,"fifoout":0,"transmitSpeed":0,"receiveSpeed":0}` if e != fmt.Sprintf("%v", v) { t.Errorf("NetIOCountersStat string is invalid: %v", v) } diff --git a/net/net_windows.go b/net/net_windows.go index 5d384342f..3082d872c 100644 --- a/net/net_windows.go +++ b/net/net_windows.go @@ -167,6 +167,8 @@ func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, c.Errout = uint64(row.OutErrors) c.Dropin = uint64(row.InDiscards) c.Dropout = uint64(row.OutDiscards) + c.TransmitSpeed = uint64(row.TransmitLinkSpeed) / 1e6 + c.ReceiveSpeed = uint64(row.ReceiveLinkSpeed) / 1e6 counters = append(counters, c) } @@ -189,6 +191,8 @@ func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, c.Errout = uint64(row.OutErrors) c.Dropin = uint64(row.InDiscards) c.Dropout = uint64(row.OutDiscards) + c.TransmitSpeed = uint64(row.Speed) / 1e6 + c.ReceiveSpeed = uint64(row.Speed) / 1e6 counters = append(counters, c) }