1
0
mirror of https://github.com/shirou/gopsutil.git synced 2025-05-10 19:29:14 +08:00

Merge pull request #109 from influxdb/nc-net-snmp

Add system wide network protocol stats
This commit is contained in:
shirou 2015-11-21 22:05:02 +09:00
commit 4e774eabc2
7 changed files with 170 additions and 0 deletions

View File

@ -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.
@ -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
================= ====== ======= ====== =======

View File

@ -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"`
@ -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)

View File

@ -3,6 +3,7 @@
package net
import (
"errors"
"os/exec"
"strconv"
"strings"
@ -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")
}

View File

@ -3,6 +3,7 @@
package net
import (
"errors"
"os/exec"
"strconv"
"strings"
@ -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")
}

View File

@ -3,6 +3,7 @@
package net
import (
"errors"
"strconv"
"strings"
@ -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
}

View File

@ -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,
@ -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

View File

@ -3,6 +3,7 @@
package net
import (
"errors"
"net"
"os"
"syscall"
@ -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")
}