Stats API to retrieve nw stats from libnetwork

- Container networking statistics are no longer
  retrievable from libcontainer after the introduction
  of libnetwork. This change adds the missing code
  for docker daemon to retireve the nw stats from
  Endpoint.

Signed-off-by: Alessandro Boch <aboch@docker.com>
This commit is contained in:
Alessandro Boch 2015-07-01 09:54:26 -07:00
parent 1d7a62d776
commit 8b40e44c39
2 changed files with 89 additions and 0 deletions

View file

@ -6,6 +6,8 @@ import (
"github.com/docker/docker/api/types"
"github.com/docker/docker/daemon/execdriver"
"github.com/docker/libcontainer"
"github.com/docker/libnetwork/sandbox"
)
type ContainerStatsConfig struct {
@ -27,6 +29,10 @@ func (daemon *Daemon) ContainerStats(name string, config *ContainerStatsConfig)
var preCpuStats types.CpuStats
getStat := func(v interface{}) *types.Stats {
update := v.(*execdriver.ResourceStats)
// Retrieve the nw statistics from libnetwork and inject them in the Stats
if nwStats, err := daemon.getNetworkStats(name); err == nil {
update.Stats.Interfaces = nwStats
}
ss := convertStatsToAPITypes(update.Stats)
ss.PreCpuStats = preCpuStats
ss.MemoryStats.Limit = uint64(update.MemoryLimit)
@ -67,3 +73,46 @@ func (daemon *Daemon) ContainerStats(name string, config *ContainerStatsConfig)
}
}
}
func (daemon *Daemon) getNetworkStats(name string) ([]*libcontainer.NetworkInterface, error) {
var list []*libcontainer.NetworkInterface
c, err := daemon.Get(name)
if err != nil {
return list, err
}
nw, err := daemon.netController.NetworkByID(c.NetworkSettings.NetworkID)
if err != nil {
return list, err
}
ep, err := nw.EndpointByID(c.NetworkSettings.EndpointID)
if err != nil {
return list, err
}
stats, err := ep.Statistics()
if err != nil {
return list, err
}
// Convert libnetwork nw stats into libcontainer nw stats
for ifName, ifStats := range stats {
list = append(list, convertLnNetworkStats(ifName, ifStats))
}
return list, nil
}
func convertLnNetworkStats(name string, stats *sandbox.InterfaceStatistics) *libcontainer.NetworkInterface {
n := &libcontainer.NetworkInterface{Name: name}
n.RxBytes = stats.RxBytes
n.RxPackets = stats.RxPackets
n.RxErrors = stats.RxErrors
n.RxDropped = stats.RxDropped
n.TxBytes = stats.TxBytes
n.TxPackets = stats.TxPackets
n.TxErrors = stats.TxErrors
n.TxDropped = stats.TxDropped
return n
}

View file

@ -3,6 +3,8 @@ package main
import (
"encoding/json"
"fmt"
"os/exec"
"strconv"
"strings"
"time"
@ -69,3 +71,41 @@ func (s *DockerSuite) TestStoppedContainerStatsGoroutines(c *check.C) {
}
}
}
func (s *DockerSuite) TestApiNetworkStats(c *check.C) {
// Run container for 30 secs
out, _ := dockerCmd(c, "run", "-d", "busybox", "top")
id := strings.TrimSpace(out)
err := waitRun(id)
c.Assert(err, check.IsNil)
// Retrieve the container address
contIP := findContainerIP(c, id)
numPings := 10
// Get the container networking stats before and after pinging the container
nwStatsPre := getNetworkStats(c, id)
_, err = exec.Command("ping", contIP, "-c", strconv.Itoa(numPings)).Output()
c.Assert(err, check.IsNil)
nwStatsPost := getNetworkStats(c, id)
// Verify the stats contain at least the expected number of packets (account for ARP)
expRxPkts := 1 + nwStatsPre.RxPackets + uint64(numPings)
expTxPkts := 1 + nwStatsPre.TxPackets + uint64(numPings)
c.Assert(nwStatsPost.TxPackets >= expTxPkts, check.Equals, true,
check.Commentf("Reported less TxPackets than expected. Expected >= %d. Found %d", expTxPkts, nwStatsPost.TxPackets))
c.Assert(nwStatsPost.RxPackets >= expRxPkts, check.Equals, true,
check.Commentf("Reported less Txbytes than expected. Expected >= %d. Found %d", expRxPkts, nwStatsPost.RxPackets))
}
func getNetworkStats(c *check.C, id string) types.Network {
var st *types.Stats
_, body, err := sockRequestRaw("GET", fmt.Sprintf("/containers/%s/stats?stream=false", id), nil, "")
c.Assert(err, check.IsNil)
err = json.NewDecoder(body).Decode(&st)
c.Assert(err, check.IsNil)
return st.Network
}