Parcourir la source

Merge pull request #14324 from aboch/ds

Stats API to retrieve nw stats from libnetwork
Alexander Morozov il y a 10 ans
Parent
commit
75864dcb38
2 fichiers modifiés avec 89 ajouts et 0 suppressions
  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
+}