Browse code

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 authored on 2015/07/02 01:54:26
Showing 2 changed files
... ...
@@ -6,6 +6,8 @@ import (
6 6
 
7 7
 	"github.com/docker/docker/api/types"
8 8
 	"github.com/docker/docker/daemon/execdriver"
9
+	"github.com/docker/libcontainer"
10
+	"github.com/docker/libnetwork/sandbox"
9 11
 )
10 12
 
11 13
 type ContainerStatsConfig struct {
... ...
@@ -27,6 +29,10 @@ func (daemon *Daemon) ContainerStats(name string, config *ContainerStatsConfig)
27 27
 	var preCpuStats types.CpuStats
28 28
 	getStat := func(v interface{}) *types.Stats {
29 29
 		update := v.(*execdriver.ResourceStats)
30
+		// Retrieve the nw statistics from libnetwork and inject them in the Stats
31
+		if nwStats, err := daemon.getNetworkStats(name); err == nil {
32
+			update.Stats.Interfaces = nwStats
33
+		}
30 34
 		ss := convertStatsToAPITypes(update.Stats)
31 35
 		ss.PreCpuStats = preCpuStats
32 36
 		ss.MemoryStats.Limit = uint64(update.MemoryLimit)
... ...
@@ -67,3 +73,46 @@ func (daemon *Daemon) ContainerStats(name string, config *ContainerStatsConfig)
67 67
 		}
68 68
 	}
69 69
 }
70
+
71
+func (daemon *Daemon) getNetworkStats(name string) ([]*libcontainer.NetworkInterface, error) {
72
+	var list []*libcontainer.NetworkInterface
73
+
74
+	c, err := daemon.Get(name)
75
+	if err != nil {
76
+		return list, err
77
+	}
78
+
79
+	nw, err := daemon.netController.NetworkByID(c.NetworkSettings.NetworkID)
80
+	if err != nil {
81
+		return list, err
82
+	}
83
+	ep, err := nw.EndpointByID(c.NetworkSettings.EndpointID)
84
+	if err != nil {
85
+		return list, err
86
+	}
87
+
88
+	stats, err := ep.Statistics()
89
+	if err != nil {
90
+		return list, err
91
+	}
92
+
93
+	// Convert libnetwork nw stats into libcontainer nw stats
94
+	for ifName, ifStats := range stats {
95
+		list = append(list, convertLnNetworkStats(ifName, ifStats))
96
+	}
97
+
98
+	return list, nil
99
+}
100
+
101
+func convertLnNetworkStats(name string, stats *sandbox.InterfaceStatistics) *libcontainer.NetworkInterface {
102
+	n := &libcontainer.NetworkInterface{Name: name}
103
+	n.RxBytes = stats.RxBytes
104
+	n.RxPackets = stats.RxPackets
105
+	n.RxErrors = stats.RxErrors
106
+	n.RxDropped = stats.RxDropped
107
+	n.TxBytes = stats.TxBytes
108
+	n.TxPackets = stats.TxPackets
109
+	n.TxErrors = stats.TxErrors
110
+	n.TxDropped = stats.TxDropped
111
+	return n
112
+}
... ...
@@ -3,6 +3,8 @@ package main
3 3
 import (
4 4
 	"encoding/json"
5 5
 	"fmt"
6
+	"os/exec"
7
+	"strconv"
6 8
 	"strings"
7 9
 	"time"
8 10
 
... ...
@@ -69,3 +71,41 @@ func (s *DockerSuite) TestStoppedContainerStatsGoroutines(c *check.C) {
69 69
 		}
70 70
 	}
71 71
 }
72
+
73
+func (s *DockerSuite) TestApiNetworkStats(c *check.C) {
74
+	// Run container for 30 secs
75
+	out, _ := dockerCmd(c, "run", "-d", "busybox", "top")
76
+	id := strings.TrimSpace(out)
77
+	err := waitRun(id)
78
+	c.Assert(err, check.IsNil)
79
+
80
+	// Retrieve the container address
81
+	contIP := findContainerIP(c, id)
82
+	numPings := 10
83
+
84
+	// Get the container networking stats before and after pinging the container
85
+	nwStatsPre := getNetworkStats(c, id)
86
+	_, err = exec.Command("ping", contIP, "-c", strconv.Itoa(numPings)).Output()
87
+	c.Assert(err, check.IsNil)
88
+	nwStatsPost := getNetworkStats(c, id)
89
+
90
+	// Verify the stats contain at least the expected number of packets (account for ARP)
91
+	expRxPkts := 1 + nwStatsPre.RxPackets + uint64(numPings)
92
+	expTxPkts := 1 + nwStatsPre.TxPackets + uint64(numPings)
93
+	c.Assert(nwStatsPost.TxPackets >= expTxPkts, check.Equals, true,
94
+		check.Commentf("Reported less TxPackets than expected. Expected >= %d. Found %d", expTxPkts, nwStatsPost.TxPackets))
95
+	c.Assert(nwStatsPost.RxPackets >= expRxPkts, check.Equals, true,
96
+		check.Commentf("Reported less Txbytes than expected. Expected >= %d. Found %d", expRxPkts, nwStatsPost.RxPackets))
97
+}
98
+
99
+func getNetworkStats(c *check.C, id string) types.Network {
100
+	var st *types.Stats
101
+
102
+	_, body, err := sockRequestRaw("GET", fmt.Sprintf("/containers/%s/stats?stream=false", id), nil, "")
103
+	c.Assert(err, check.IsNil)
104
+
105
+	err = json.NewDecoder(body).Decode(&st)
106
+	c.Assert(err, check.IsNil)
107
+
108
+	return st.Network
109
+}