Skip to content

Commit

Permalink
Merge pull request #109 from influxdb/nc-net-snmp
Browse files Browse the repository at this point in the history
Add system wide network protocol stats
  • Loading branch information
shirou committed Nov 21, 2015
2 parents 4cd1080 + da832b3 commit 4e774ea
Show file tree
Hide file tree
Showing 7 changed files with 170 additions and 0 deletions.
6 changes: 6 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,11 @@ Several methods have been added which are not present in psutil, but will provid

- various status

- net_protocols (linux only)

- system wide stats on network protocols (i.e IP, TCP, UDP, etc.)
- sourced from /proc/net/snmp

Some codes are ported from Ohai. many thanks.


Expand All @@ -145,6 +150,7 @@ users x x x x
pids x x x x
pid_exists x x x x
net_connections x x
net_protocols x
net_if_addrs
net_if_stats
================= ====== ======= ====== =======
Expand Down
11 changes: 11 additions & 0 deletions net/net.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ type NetConnectionStat struct {
Pid int32 `json:"pid"`
}

// System wide stats about different network protocols
type NetProtoCountersStat struct {
Protocol string `json:"protocol"`
Stats map[string]int64 `json:"stats"`
}

// NetInterfaceAddr is designed for represent interface addresses
type NetInterfaceAddr struct {
Addr string `json:"addr"`
Expand Down Expand Up @@ -75,6 +81,11 @@ func (n NetConnectionStat) String() string {
return string(s)
}

func (n NetProtoCountersStat) String() string {
s, _ := json.Marshal(n)
return string(s)
}

func (a Addr) String() string {
s, _ := json.Marshal(a)
return string(s)
Expand Down
9 changes: 9 additions & 0 deletions net/net_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
package net

import (
"errors"
"os/exec"
"strconv"
"strings"
Expand Down Expand Up @@ -90,3 +91,11 @@ func NetIOCounters(pernic bool) ([]NetIOCountersStat, error) {

return ret, nil
}

// NetProtoCounters returns network statistics for the entire system
// If protocols is empty then all protocols are returned, otherwise
// just the protocols in the list are returned.
// Not Implemented for Darwin
func NetProtoCounters(protocols []string) ([]NetProtoCountersStat, error) {
return nil, errors.New("NetProtoCounters not implemented for darwin")
}
9 changes: 9 additions & 0 deletions net/net_freebsd.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
package net

import (
"errors"
"os/exec"
"strconv"
"strings"
Expand Down Expand Up @@ -81,3 +82,11 @@ func NetIOCounters(pernic bool) ([]NetIOCountersStat, error) {

return ret, nil
}

// NetProtoCounters returns network statistics for the entire system
// If protocols is empty then all protocols are returned, otherwise
// just the protocols in the list are returned.
// Not Implemented for FreeBSD
func NetProtoCounters(protocols []string) ([]NetProtoCountersStat, error) {
return nil, errors.New("NetProtoCounters not implemented for freebsd")
}
71 changes: 71 additions & 0 deletions net/net_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
package net

import (
"errors"
"strconv"
"strings"

Expand Down Expand Up @@ -89,3 +90,73 @@ func NetIOCounters(pernic bool) ([]NetIOCountersStat, error) {

return ret, nil
}

var netProtocols = []string{
"ip",
"icmp",
"icmpmsg",
"tcp",
"udp",
"udplite",
}

// NetProtoCounters returns network statistics for the entire system
// If protocols is empty then all protocols are returned, otherwise
// just the protocols in the list are returned.
// Available protocols:
// ip,icmp,icmpmsg,tcp,udp,udplite
func NetProtoCounters(protocols []string) ([]NetProtoCountersStat, error) {
if len(protocols) == 0 {
protocols = netProtocols
}

stats := make([]NetProtoCountersStat, 0, len(protocols))
protos := make(map[string]bool, len(protocols))
for _, p := range protocols {
protos[p] = true
}

filename := "/proc/net/snmp"
lines, err := common.ReadLines(filename)
if err != nil {
return nil, err
}

linecount := len(lines)
for i := 0; i < linecount; i++ {
line := lines[i]
r := strings.IndexRune(line, ':')
if r == -1 {
return nil, errors.New(filename + " is not fomatted correctly, expected ':'.")
}
proto := strings.ToLower(line[:r])
if !protos[proto] {
// skip protocol and data line
i++
continue
}

// Read header line
statNames := strings.Split(line[r+2:], " ")

// Read data line
i++
statValues := strings.Split(lines[i][r+2:], " ")
if len(statNames) != len(statValues) {
return nil, errors.New(filename + " is not fomatted correctly, expected same number of columns.")
}
stat := NetProtoCountersStat{
Protocol: proto,
Stats: make(map[string]int64, len(statNames)),
}
for j := range statNames {
value, err := strconv.ParseInt(statValues[j], 10, 64)
if err != nil {
return nil, err
}
stat.Stats[statNames[j]] = value
}
stats = append(stats, stat)
}
return stats, nil
}
55 changes: 55 additions & 0 deletions net/net_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,22 @@ func TestNetIOCountersStatString(t *testing.T) {
}
}

func TestNetProtoCountersStatString(t *testing.T) {
v := NetProtoCountersStat{
Protocol: "tcp",
Stats: map[string]int64{
"MaxConn": -1,
"ActiveOpens": 4000,
"PassiveOpens": 3000,
},
}
e := `{"protocol":"tcp","stats":{"ActiveOpens":4000,"MaxConn":-1,"PassiveOpens":3000}}`
if e != fmt.Sprintf("%v", v) {
t.Errorf("NetProtoCountersStat string is invalid: %v", v)
}

}

func TestNetConnectionStatString(t *testing.T) {
v := NetConnectionStat{
Fd: 10,
Expand Down Expand Up @@ -122,6 +138,45 @@ func TestNetInterfaces(t *testing.T) {
}
}

func TestNetProtoCountersStatsAll(t *testing.T) {
v, err := NetProtoCounters(nil)
if err != nil {
t.Fatalf("Could not get NetProtoCounters: %v", err)
}
if len(v) == 0 {
t.Fatalf("Could not get NetProtoCounters: %v", err)
}
for _, vv := range v {
if vv.Protocol == "" {
t.Errorf("Invalid NetProtoCountersStat: %v", vv)
}
if len(vv.Stats) == 0 {
t.Errorf("Invalid NetProtoCountersStat: %v", vv)
}
}
}

func TestNetProtoCountersStats(t *testing.T) {
v, err := NetProtoCounters([]string{"tcp", "ip"})
if err != nil {
t.Fatalf("Could not get NetProtoCounters: %v", err)
}
if len(v) == 0 {
t.Fatalf("Could not get NetProtoCounters: %v", err)
}
if len(v) != 2 {
t.Fatalf("Go incorrect number of NetProtoCounters: %v", err)
}
for _, vv := range v {
if vv.Protocol != "tcp" && vv.Protocol != "ip" {
t.Errorf("Invalid NetProtoCountersStat: %v", vv)
}
if len(vv.Stats) == 0 {
t.Errorf("Invalid NetProtoCountersStat: %v", vv)
}
}
}

func TestNetConnections(t *testing.T) {
if ci := os.Getenv("CI"); ci != "" { // skip if test on drone.io
return
Expand Down
9 changes: 9 additions & 0 deletions net/net_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
package net

import (
"errors"
"net"
"os"
"syscall"
Expand Down Expand Up @@ -96,3 +97,11 @@ func getAdapterList() (*syscall.IpAdapterInfo, error) {
}
return a, nil
}

// NetProtoCounters returns network statistics for the entire system
// If protocols is empty then all protocols are returned, otherwise
// just the protocols in the list are returned.
// Not Implemented for Windows
func NetProtoCounters(protocols []string) ([]NetProtoCountersStat, error) {
return nil, errors.New("NetProtoCounters not implemented for windows")
}

0 comments on commit 4e774ea

Please sign in to comment.