From 45334c6f71f704ac1632168ca543e36931adce0e Mon Sep 17 00:00:00 2001 From: Rohit Jnagal Date: Tue, 30 Dec 2014 17:12:42 +0000 Subject: [PATCH] Add network stats to root container. Provides machine-level networking stats. --- container/raw/handler.go | 35 +++++++++++++++++++++++++ utils/sysfs/fakesysfs/fake.go | 4 +++ utils/sysfs/sysfs.go | 15 +++++++++++ utils/sysinfo/sysinfo.go | 48 +++++++++++++++++++++++++++++++++++ utils/sysinfo/sysinfo_test.go | 22 ++++++++++++++++ 5 files changed, 124 insertions(+) diff --git a/container/raw/handler.go b/container/raw/handler.go index df999690..4ea5c336 100644 --- a/container/raw/handler.go +++ b/container/raw/handler.go @@ -33,6 +33,7 @@ import ( "github.com/google/cadvisor/fs" "github.com/google/cadvisor/info" "github.com/google/cadvisor/utils" + "github.com/google/cadvisor/utils/sysinfo" ) type rawContainerHandler struct { @@ -145,6 +146,18 @@ func readInt64(dirpath string, file string) uint64 { return val } +func (self *rawContainerHandler) GetRootNetworkDevices() ([]info.NetInfo, error) { + nd := []info.NetInfo{} + if self.name == "/" { + mi, err := self.machineInfoFactory.GetMachineInfo() + if err != nil { + return nd, err + } + return mi.NetworkDevices, nil + } + return nd, nil +} + func (self *rawContainerHandler) GetSpec() (info.ContainerSpec, error) { var spec info.ContainerSpec @@ -197,6 +210,15 @@ func (self *rawContainerHandler) GetSpec() (info.ContainerSpec, error) { if self.networkInterface != nil { spec.HasNetwork = true } + + // Check physical network devices for root container. + nd, err := self.GetRootNetworkDevices() + if err != nil { + return spec, err + } + if len(nd) != 0 { + spec.HasNetwork = true + } return spec, nil } @@ -283,6 +305,19 @@ func (self *rawContainerHandler) GetStats() (*info.ContainerStats, error) { return nil, err } + // Fill in network stats for root. + nd, err := self.GetRootNetworkDevices() + if err != nil { + return stats, err + } + if len(nd) != 0 { + // ContainerStats only reports stat for one network device. + // TODO(rjnagal): Handle multiple physical network devices. + stats.Network, err = sysinfo.GetNetworkStats(nd[0].Name) + if err != nil { + return stats, err + } + } return stats, nil } diff --git a/utils/sysfs/fakesysfs/fake.go b/utils/sysfs/fakesysfs/fake.go index e6e530d8..3738c3de 100644 --- a/utils/sysfs/fakesysfs/fake.go +++ b/utils/sysfs/fakesysfs/fake.go @@ -85,6 +85,10 @@ func (self *FakeSysFs) GetNetworkSpeed(name string) (string, error) { return "1000\n", nil } +func (self *FakeSysFs) GetNetworkStatValue(name string, stat string) (uint64, error) { + return 1024, nil +} + func (self *FakeSysFs) GetCaches(id int) ([]os.FileInfo, error) { self.info.EntryName = "index0" return []os.FileInfo{&self.info}, nil diff --git a/utils/sysfs/sysfs.go b/utils/sysfs/sysfs.go index a4c6c078..6a454ff3 100644 --- a/utils/sysfs/sysfs.go +++ b/utils/sysfs/sysfs.go @@ -52,6 +52,7 @@ type SysFs interface { GetNetworkAddress(string) (string, error) GetNetworkMtu(string) (string, error) GetNetworkSpeed(string) (string, error) + GetNetworkStatValue(dev string, stat string) (uint64, error) // Get directory information for available caches accessible to given cpu. GetCaches(id int) ([]os.FileInfo, error) @@ -113,6 +114,20 @@ func (self *realSysFs) GetNetworkSpeed(name string) (string, error) { return string(speed), nil } +func (self *realSysFs) GetNetworkStatValue(dev string, stat string) (uint64, error) { + statPath := path.Join(netDir, dev, "/statistics", stat) + out, err := ioutil.ReadFile(statPath) + if err != nil { + return 0, fmt.Errorf("failed to read stat from %q for device %q", statPath, dev) + } + var s uint64 + n, err := fmt.Sscanf(string(out), "%d", &s) + if err != nil || n != 1 { + return 0, fmt.Errorf("could not parse value from %q for file %s", string(out), statPath) + } + return s, nil +} + func (self *realSysFs) GetCaches(id int) ([]os.FileInfo, error) { cpuPath := fmt.Sprintf("%s%d/cache", cacheDir, id) return ioutil.ReadDir(cpuPath) diff --git a/utils/sysinfo/sysinfo.go b/utils/sysinfo/sysinfo.go index 14ab1080..fac863d4 100644 --- a/utils/sysinfo/sysinfo.go +++ b/utils/sysinfo/sysinfo.go @@ -133,3 +133,51 @@ func GetCacheInfo(sysFs sysfs.SysFs, id int) ([]sysfs.CacheInfo, error) { } return info, nil } + +func GetNetworkStats(name string) (info.NetworkStats, error) { + stats := info.NetworkStats{} + // TODO(rjnagal): Take syfs as an argument. + sysFs, err := sysfs.NewRealSysFs() + if err != nil { + return stats, err + } + return getNetworkStats(name, sysFs) +} + +func getNetworkStats(name string, sysFs sysfs.SysFs) (info.NetworkStats, error) { + stats := info.NetworkStats{} + var err error + stats.RxBytes, err = sysFs.GetNetworkStatValue(name, "rx_bytes") + if err != nil { + return stats, err + } + stats.RxPackets, err = sysFs.GetNetworkStatValue(name, "rx_packets") + if err != nil { + return stats, err + } + stats.RxErrors, err = sysFs.GetNetworkStatValue(name, "rx_errors") + if err != nil { + return stats, err + } + stats.RxDropped, err = sysFs.GetNetworkStatValue(name, "rx_dropped") + if err != nil { + return stats, err + } + stats.TxBytes, err = sysFs.GetNetworkStatValue(name, "tx_bytes") + if err != nil { + return stats, err + } + stats.TxPackets, err = sysFs.GetNetworkStatValue(name, "tx_packets") + if err != nil { + return stats, err + } + stats.TxErrors, err = sysFs.GetNetworkStatValue(name, "tx_errors") + if err != nil { + return stats, err + } + stats.TxDropped, err = sysFs.GetNetworkStatValue(name, "tx_dropped") + if err != nil { + return stats, err + } + return stats, nil +} diff --git a/utils/sysinfo/sysinfo_test.go b/utils/sysinfo/sysinfo_test.go index c5c3d370..babe955b 100644 --- a/utils/sysinfo/sysinfo_test.go +++ b/utils/sysinfo/sysinfo_test.go @@ -17,6 +17,7 @@ package sysinfo import ( "testing" + "github.com/google/cadvisor/info" "github.com/google/cadvisor/utils/sysfs" "github.com/google/cadvisor/utils/sysfs/fakesysfs" ) @@ -88,3 +89,24 @@ func TestGetCacheInfo(t *testing.T) { t.Errorf("expected to find cacheinfo %+v. Got %+v", cacheInfo, caches[0]) } } + +func TestGetNetworkStats(t *testing.T) { + expected_stats := info.NetworkStats{ + RxBytes: 1024, + RxPackets: 1024, + RxErrors: 1024, + RxDropped: 1024, + TxBytes: 1024, + TxPackets: 1024, + TxErrors: 1024, + TxDropped: 1024, + } + fakeSys := &fakesysfs.FakeSysFs{} + netStats, err := getNetworkStats("eth0", fakeSys) + if err != nil { + t.Errorf("call to getNetworkStats() failed with %s", err) + } + if expected_stats != netStats { + t.Errorf("expected to get stats %+v, got %+v", expected_stats, netStats) + } +}