Bläddra i källkod

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>

Alessandro Boch 10 år sedan
förälder
incheckning
8b40e44c39
2 ändrade filer med 89 tillägg och 0 borttagningar
  1. 49 0
      daemon/stats.go
  2. 40 0
      integration-cli/docker_api_stats_test.go

+ 49 - 0
daemon/stats.go

@@ -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
+}

+ 40 - 0
integration-cli/docker_api_stats_test.go

@@ -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
+}