Browse Source

Merge pull request #346 from aboch/is

Endpoint to expose interfaces' statistics
Madhu Venugopal 10 years ago
parent
commit
26dc363928

+ 30 - 0
libnetwork/endpoint.go

@@ -51,6 +51,9 @@ type Endpoint interface {
 
 	// Delete and detaches this endpoint from the network.
 	Delete() error
+
+	// Retrieve the interfaces' statistics from the sandbox
+	Statistics() (map[string]*sandbox.InterfaceStatistics, error)
 }
 
 // EndpointOption is a option setter function type used to pass varios options to Network
@@ -557,6 +560,33 @@ func (ep *endpoint) Delete() error {
 	return nil
 }
 
+func (ep *endpoint) Statistics() (map[string]*sandbox.InterfaceStatistics, error) {
+	m := make(map[string]*sandbox.InterfaceStatistics)
+
+	ep.Lock()
+	n := ep.network
+	skey := ep.container.data.SandboxKey
+	ep.Unlock()
+
+	n.Lock()
+	c := n.ctrlr
+	n.Unlock()
+
+	sbox := c.sandboxGet(skey)
+	if sbox == nil {
+		return m, nil
+	}
+
+	var err error
+	for _, i := range sbox.Info().Interfaces() {
+		if m[i.DstName()], err = i.Statistics(); err != nil {
+			return m, err
+		}
+	}
+
+	return m, nil
+}
+
 func (ep *endpoint) deleteEndpoint() error {
 	ep.Lock()
 	n := ep.network

+ 9 - 0
libnetwork/libnetwork_test.go

@@ -1042,6 +1042,15 @@ func TestEndpointJoin(t *testing.T) {
 		t.Fatalf("Endpoint ContainerInfo returned unexpected id: %s", ep1.ContainerInfo().ID())
 	}
 
+	// Attempt retrieval of endpoint interfaces statistics
+	stats, err := ep1.Statistics()
+	if err != nil {
+		t.Fatal(err)
+	}
+	if _, ok := stats["eth0"]; !ok {
+		t.Fatalf("Did not find eth0 statistics")
+	}
+
 	// Now test the container joining another network
 	n2, err := createTestNetwork(bridgeNetType, "testnetwork2",
 		options.Generic{

+ 54 - 0
libnetwork/sandbox/interface_linux.go

@@ -2,7 +2,9 @@ package sandbox
 
 import (
 	"fmt"
+	"io/ioutil"
 	"net"
+	"regexp"
 	"sync"
 
 	"github.com/docker/libnetwork/types"
@@ -153,6 +155,33 @@ func (i *nwIface) Remove() error {
 	})
 }
 
+// Returns the sandbox's side veth interface statistics
+func (i *nwIface) Statistics() (*InterfaceStatistics, error) {
+	i.Lock()
+	n := i.ns
+	i.Unlock()
+
+	n.Lock()
+	path := n.path
+	n.Unlock()
+
+	s := &InterfaceStatistics{}
+
+	err := nsInvoke(path, func(nsFD int) error { return nil }, func(callerFD int) error {
+		data, err := ioutil.ReadFile(netStatsFile)
+		if err != nil {
+			return fmt.Errorf("failed to open %s: %v", netStatsFile, err)
+		}
+		return scanInterfaceStats(string(data), i.DstName(), s)
+	})
+
+	if err != nil {
+		err = fmt.Errorf("failed to retrieve the statistics for %s in netns %s: %v", i.DstName(), path, err)
+	}
+
+	return s, err
+}
+
 func (n *networkNamespace) findDst(srcName string, isBridge bool) string {
 	n.Lock()
 	defer n.Unlock()
@@ -311,3 +340,28 @@ func setInterfaceRoutes(iface netlink.Link, i *nwIface) error {
 	}
 	return nil
 }
+
+// In older kernels (like the one in Centos 6.6 distro) sysctl does not have netns support. Therefore
+// we cannot gather the statistics from /sys/class/net/<dev>/statistics/<counter> files. Per-netns stats
+// are naturally found in /proc/net/dev in kernels which support netns (ifconfig relyes on that).
+const (
+	netStatsFile = "/proc/net/dev"
+	base         = "[ ]*%s:([ ]+[0-9]+){16}"
+)
+
+func scanInterfaceStats(data, ifName string, i *InterfaceStatistics) error {
+	var (
+		bktStr string
+		bkt    uint64
+	)
+
+	regex := fmt.Sprintf(base, ifName)
+	re := regexp.MustCompile(regex)
+	line := re.FindString(data)
+
+	_, err := fmt.Sscanf(line, "%s %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d",
+		&bktStr, &i.RxBytes, &i.RxPackets, &i.RxErrors, &i.RxDropped, &bkt, &bkt, &bkt,
+		&bkt, &i.TxBytes, &i.TxPackets, &i.TxErrors, &i.TxDropped, &bkt, &bkt, &bkt, &bkt)
+
+	return err
+}

+ 21 - 0
libnetwork/sandbox/sandbox.go

@@ -1,6 +1,7 @@
 package sandbox
 
 import (
+	"fmt"
 	"net"
 
 	"github.com/docker/libnetwork/types"
@@ -146,4 +147,24 @@ type Interface interface {
 	// Remove an interface from the sandbox by renaming to original name
 	// and moving it out of the sandbox.
 	Remove() error
+
+	// Statistics returns the statistics for this interface
+	Statistics() (*InterfaceStatistics, error)
+}
+
+// InterfaceStatistics represents the interface's statistics
+type InterfaceStatistics struct {
+	RxBytes   uint64
+	RxPackets uint64
+	RxErrors  uint64
+	RxDropped uint64
+	TxBytes   uint64
+	TxPackets uint64
+	TxErrors  uint64
+	TxDropped uint64
+}
+
+func (is *InterfaceStatistics) String() string {
+	return fmt.Sprintf("\nRxBytes: %d, RxPackets: %d, RxErrors: %d, RxDropped: %d, TxBytes: %d, TxPackets: %d, TxErrors: %d, TxDropped: %d",
+		is.RxBytes, is.RxPackets, is.RxErrors, is.RxDropped, is.TxBytes, is.TxPackets, is.TxErrors, is.TxDropped)
 }

+ 26 - 0
libnetwork/sandbox/sandbox_linux_test.go

@@ -152,3 +152,29 @@ func verifyCleanup(t *testing.T, s Sandbox, wait bool) {
 		}
 	}
 }
+
+func TestScanStatistics(t *testing.T) {
+	data :=
+		"Inter-|   Receive                                                |  Transmit\n" +
+			"	face |bytes    packets errs drop fifo frame compressed multicast|bytes    packets errs drop fifo colls carrier compressed\n" +
+			"  eth0:       0       0    0    0    0     0          0         0        0       0    0    0    0     0       0          0\n" +
+			" wlan0: 7787685   11141    0    0    0     0          0         0  1681390    7220    0    0    0     0       0          0\n" +
+			"    lo:  783782    1853    0    0    0     0          0         0   783782    1853    0    0    0     0       0          0\n" +
+			"lxcbr0:       0       0    0    0    0     0          0         0     9006      61    0    0    0     0       0          0\n"
+
+	i := &InterfaceStatistics{}
+
+	if err := scanInterfaceStats(data, "wlan0", i); err != nil {
+		t.Fatal(err)
+	}
+	if i.TxBytes != 1681390 || i.TxPackets != 7220 || i.RxBytes != 7787685 || i.RxPackets != 11141 {
+		t.Fatalf("Error scanning the statistics")
+	}
+
+	if err := scanInterfaceStats(data, "lxcbr0", i); err != nil {
+		t.Fatal(err)
+	}
+	if i.TxBytes != 9006 || i.TxPackets != 61 || i.RxBytes != 0 || i.RxPackets != 0 {
+		t.Fatalf("Error scanning the statistics")
+	}
+}