From b923eff11ce39b13a5cccc3ae9d1737198f55551 Mon Sep 17 00:00:00 2001 From: Victor Marmol Date: Thu, 4 Jun 2015 10:58:32 -0700 Subject: [PATCH] Expose multiple network interfaces in API. Part of #686 --- api/versions.go | 3 +-- container/docker/handler.go | 30 +++++++++++++-------- container/libcontainer/helpers.go | 43 +++++++++++++++++++++---------- info/v1/container.go | 9 ++++++- info/v2/container.go | 9 +++++-- metrics/prometheus_test.go | 19 ++++++++------ utils/sysinfo/sysinfo.go | 10 +++---- utils/sysinfo/sysinfo_test.go | 3 ++- 8 files changed, 82 insertions(+), 44 deletions(-) diff --git a/api/versions.go b/api/versions.go index bba8be5121..bac878e4fd 100644 --- a/api/versions.go +++ b/api/versions.go @@ -426,8 +426,7 @@ func convertStats(cont *info.ContainerInfo) []v2.ContainerStats { stat.Memory = val.Memory } if stat.HasNetwork { - // TODO(rjnagal): Return stats about all network interfaces. - stat.Network = append(stat.Network, val.Network) + stat.Network.Interfaces = val.Network.Interfaces } if stat.HasFilesystem { stat.Filesystem = val.Filesystem diff --git a/container/docker/handler.go b/container/docker/handler.go index 2e3f8cf455..6d8ca8c7f0 100644 --- a/container/docker/handler.go +++ b/container/docker/handler.go @@ -261,17 +261,10 @@ func (self *dockerContainerHandler) GetStats() (*info.ContainerStats, error) { } // TODO(rjnagal): Remove the conversion when network stats are read from libcontainer. - net := stats.Network - // Ingress for host veth is from the container. - // Hence tx_bytes stat on the host veth is actually number of bytes received by the container. - stats.Network.RxBytes = net.TxBytes - stats.Network.RxPackets = net.TxPackets - stats.Network.RxErrors = net.TxErrors - stats.Network.RxDropped = net.TxDropped - stats.Network.TxBytes = net.RxBytes - stats.Network.TxPackets = net.RxPackets - stats.Network.TxErrors = net.RxErrors - stats.Network.TxDropped = net.RxDropped + convertInterfaceStats(&stats.Network.InterfaceStats) + for i := range stats.Network.Interfaces { + convertInterfaceStats(&stats.Network.Interfaces[i]) + } // Get filesystem stats. err = self.getFsStats(stats) @@ -282,6 +275,21 @@ func (self *dockerContainerHandler) GetStats() (*info.ContainerStats, error) { return stats, nil } +func convertInterfaceStats(stats *info.InterfaceStats) { + net := stats + + // Ingress for host veth is from the container. + // Hence tx_bytes stat on the host veth is actually number of bytes received by the container. + stats.RxBytes = net.TxBytes + stats.RxPackets = net.TxPackets + stats.RxErrors = net.TxErrors + stats.RxDropped = net.TxDropped + stats.TxBytes = net.RxBytes + stats.TxPackets = net.RxPackets + stats.TxErrors = net.RxErrors + stats.TxDropped = net.RxDropped +} + func (self *dockerContainerHandler) ListContainers(listType container.ListType) ([]info.ContainerReference, error) { if self.name != "/docker" { return []info.ContainerReference{}, nil diff --git a/container/libcontainer/helpers.go b/container/libcontainer/helpers.go index 53e41af858..bb6e9ee309 100644 --- a/container/libcontainer/helpers.go +++ b/container/libcontainer/helpers.go @@ -84,15 +84,20 @@ func GetStats(cgroupManager cgroups.Manager, networkInterfaces []string) (*info. } stats := toContainerStats(libcontainerStats) - if len(networkInterfaces) != 0 { - // ContainerStats only reports stat for one network device. - // TODO(rjnagal): Handle multiple physical network devices. - // TODO(rjnagal): Use networking stats directly from libcontainer. - stats.Network, err = sysinfo.GetNetworkStats(networkInterfaces[0]) + // TODO(rjnagal): Use networking stats directly from libcontainer. + stats.Network.Interfaces = make([]info.InterfaceStats, len(networkInterfaces)) + for i := range networkInterfaces { + interfaceStats, err := sysinfo.GetNetworkStats(networkInterfaces[i]) if err != nil { return stats, err } + stats.Network.Interfaces[i] = interfaceStats } + // For backwards compatability. + if len(networkInterfaces) > 0 { + stats.Network.InterfaceStats = stats.Network.Interfaces[0] + } + return stats, nil } @@ -213,15 +218,25 @@ func toContainerStats2(s *cgroups.Stats, ret *info.ContainerStats) { } func toContainerStats3(libcontainerStats *libcontainer.Stats, ret *info.ContainerStats) { - // TODO(vmarmol): Handle multiple interfaces. - ret.Network.RxBytes = libcontainerStats.Interfaces[0].RxBytes - ret.Network.RxPackets = libcontainerStats.Interfaces[0].RxPackets - ret.Network.RxErrors = libcontainerStats.Interfaces[0].RxErrors - ret.Network.RxDropped = libcontainerStats.Interfaces[0].RxDropped - ret.Network.TxBytes = libcontainerStats.Interfaces[0].TxBytes - ret.Network.TxPackets = libcontainerStats.Interfaces[0].TxPackets - ret.Network.TxErrors = libcontainerStats.Interfaces[0].TxErrors - ret.Network.TxDropped = libcontainerStats.Interfaces[0].TxDropped + ret.Network.Interfaces = make([]info.InterfaceStats, len(libcontainerStats.Interfaces)) + for i := range libcontainerStats.Interfaces { + ret.Network.Interfaces[i] = info.InterfaceStats{ + Name: libcontainerStats.Interfaces[i].Name, + RxBytes: libcontainerStats.Interfaces[i].RxBytes, + RxPackets: libcontainerStats.Interfaces[i].RxPackets, + RxErrors: libcontainerStats.Interfaces[i].RxErrors, + RxDropped: libcontainerStats.Interfaces[i].RxDropped, + TxBytes: libcontainerStats.Interfaces[i].TxBytes, + TxPackets: libcontainerStats.Interfaces[i].TxPackets, + TxErrors: libcontainerStats.Interfaces[i].TxErrors, + TxDropped: libcontainerStats.Interfaces[i].TxDropped, + } + } + + // Add to base struct for backwards compatability. + if len(ret.Network.Interfaces) > 0 { + ret.Network.InterfaceStats = ret.Network.Interfaces[0] + } } func toContainerStats(libcontainerStats *libcontainer.Stats) *info.ContainerStats { diff --git a/info/v1/container.go b/info/v1/container.go index 0e4b89bb38..e9afe26a71 100644 --- a/info/v1/container.go +++ b/info/v1/container.go @@ -312,7 +312,9 @@ type MemoryStatsMemoryData struct { Pgmajfault uint64 `json:"pgmajfault"` } -type NetworkStats struct { +type InterfaceStats struct { + // The name of the interface. + Name string `json:"name"` // Cumulative count of bytes received. RxBytes uint64 `json:"rx_bytes"` // Cumulative count of packets received. @@ -331,6 +333,11 @@ type NetworkStats struct { TxDropped uint64 `json:"tx_dropped"` } +type NetworkStats struct { + InterfaceStats `json:",inline"` + Interfaces []InterfaceStats `json:"interfaces,omitempty"` +} + type FsStats struct { // The block device name associated with the filesystem. Device string `json:"device,omitempty"` diff --git a/info/v2/container.go b/info/v2/container.go index 6ba37699a2..7d9c388ce1 100644 --- a/info/v2/container.go +++ b/info/v2/container.go @@ -92,8 +92,8 @@ type ContainerStats struct { HasMemory bool `json:"has_memory"` Memory v1.MemoryStats `json:"memory,omitempty"` // Network statistics - HasNetwork bool `json:"has_network"` - Network []v1.NetworkStats `json:"network,omitempty"` + HasNetwork bool `json:"has_network"` + Network NetworkStats `json:"network,omitempty"` // Filesystem statistics HasFilesystem bool `json:"has_filesystem"` Filesystem []v1.FsStats `json:"filesystem,omitempty"` @@ -189,3 +189,8 @@ type ProcessInfo struct { CgroupPath string `json:"cgroup_path"` Cmd string `json:"cmd"` } + +type NetworkStats struct { + // Network stats by interface. + Interfaces []v1.InterfaceStats `json:"interfaces,omitempty"` +} diff --git a/metrics/prometheus_test.go b/metrics/prometheus_test.go index ac70189101..3efbe043cc 100644 --- a/metrics/prometheus_test.go +++ b/metrics/prometheus_test.go @@ -57,14 +57,17 @@ func (p testSubcontainersInfoProvider) SubcontainersInfo(string, *info.Container }, }, Network: info.NetworkStats{ - RxBytes: 14, - RxPackets: 15, - RxErrors: 16, - RxDropped: 17, - TxBytes: 18, - TxPackets: 19, - TxErrors: 20, - TxDropped: 21, + InterfaceStats: info.InterfaceStats{ + Name: "eth0", + RxBytes: 14, + RxPackets: 15, + RxErrors: 16, + RxDropped: 17, + TxBytes: 18, + TxPackets: 19, + TxErrors: 20, + TxDropped: 21, + }, }, Filesystem: []info.FsStats{ { diff --git a/utils/sysinfo/sysinfo.go b/utils/sysinfo/sysinfo.go index 9d6a6208d8..27017389e9 100644 --- a/utils/sysinfo/sysinfo.go +++ b/utils/sysinfo/sysinfo.go @@ -157,19 +157,19 @@ func GetCacheInfo(sysFs sysfs.SysFs, id int) ([]sysfs.CacheInfo, error) { return info, nil } -func GetNetworkStats(name string) (info.NetworkStats, error) { - stats := info.NetworkStats{} +func GetNetworkStats(name string) (info.InterfaceStats, error) { // TODO(rjnagal): Take syfs as an argument. sysFs, err := sysfs.NewRealSysFs() if err != nil { - return stats, err + return info.InterfaceStats{}, err } return getNetworkStats(name, sysFs) } -func getNetworkStats(name string, sysFs sysfs.SysFs) (info.NetworkStats, error) { - stats := info.NetworkStats{} +func getNetworkStats(name string, sysFs sysfs.SysFs) (info.InterfaceStats, error) { + var stats info.InterfaceStats var err error + stats.Name = name stats.RxBytes, err = sysFs.GetNetworkStatValue(name, "rx_bytes") if err != nil { return stats, err diff --git a/utils/sysinfo/sysinfo_test.go b/utils/sysinfo/sysinfo_test.go index f46a0c14f9..97df16b9c9 100644 --- a/utils/sysinfo/sysinfo_test.go +++ b/utils/sysinfo/sysinfo_test.go @@ -110,7 +110,8 @@ func TestGetCacheInfo(t *testing.T) { } func TestGetNetworkStats(t *testing.T) { - expected_stats := info.NetworkStats{ + expected_stats := info.InterfaceStats{ + Name: "eth0", RxBytes: 1024, RxPackets: 1024, RxErrors: 1024,