Remove network package

Also add ability to get network stats from multiple interfaces.

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
Michael Crosby 2015-02-09 15:16:27 -08:00
parent 6a04779b41
commit ad49d71504
11 changed files with 239 additions and 358 deletions

View File

@ -6,16 +6,9 @@ package libcontainer
import (
"os"
"github.com/docker/libcontainer/cgroups"
"github.com/docker/libcontainer/configs"
"github.com/docker/libcontainer/network"
)
type Stats struct {
NetworkStats *network.NetworkStats `json:"network_stats,omitempty"`
CgroupStats *cgroups.Stats `json:"cgroup_stats,omitempty"`
}
// A libcontainer container object.
//
// Each container is thread-safe within the same process. Since a container can

View File

@ -10,14 +10,9 @@ import (
"github.com/docker/libcontainer/cgroups"
"github.com/docker/libcontainer/configs"
"github.com/docker/libcontainer/network"
"github.com/golang/glog"
)
type pid struct {
Pid int `json:"Pid"`
}
type linuxContainer struct {
id string
root string
@ -73,13 +68,14 @@ func (c *linuxContainer) Stats() (*Stats, error) {
if stats.CgroupStats, err = c.cgroupManager.GetStats(); err != nil {
return stats, newGenericError(err, SystemError)
}
// TODO: handle stats for multiple veth interfaces
for _, iface := range c.config.Networks {
if iface.Type == "veth" {
if stats.NetworkStats, err = network.GetStats(iface.VethHost); err != nil {
switch iface.Type {
case "veth":
istats, err := getNetworkInterfaceStats(iface.VethHost)
if err != nil {
return stats, newGenericError(err, SystemError)
}
break
stats.Interfaces = append(stats.Interfaces, istats)
}
}
return stats, nil

View File

@ -11,7 +11,6 @@ import (
"github.com/docker/libcontainer/configs"
"github.com/docker/libcontainer/netlink"
"github.com/docker/libcontainer/network"
"github.com/docker/libcontainer/security/capabilities"
"github.com/docker/libcontainer/system"
"github.com/docker/libcontainer/user"
@ -27,13 +26,17 @@ const (
initUsernsSetup initType = "userns_setup"
)
type pid struct {
Pid int `json:"pid"`
}
// Process is used for transferring parameters from Exec() to Init()
type initConfig struct {
Args []string `json:"args,omitempty"`
Env []string `json:"env,omitempty"`
Cwd string `json:"cwd,omitempty"`
User string `json:"user,omitempty"`
Config *configs.Config `json:"config,omitempty"`
Args []string `json:"args"`
Env []string `json:"env"`
Cwd string `json:"cwd"`
User string `json:"user"`
Config *configs.Config `json:"config"`
}
type initer interface {
@ -183,7 +186,7 @@ func setupUser(config *initConfig) error {
// setting the MTU and IP address along with the default gateway
func setupNetwork(config *configs.Config) error {
for _, config := range config.Networks {
strategy, err := network.GetStrategy(config.Type)
strategy, err := getStrategy(config.Type)
if err != nil {
return err
}

201
linux_network.go Normal file
View File

@ -0,0 +1,201 @@
// +build linux
package libcontainer
import (
"errors"
"fmt"
"io/ioutil"
"net"
"path/filepath"
"strconv"
"strings"
"github.com/docker/libcontainer/configs"
"github.com/docker/libcontainer/netlink"
)
const defaultVethInterfaceName = "eth0"
var (
ErrNotValidStrategyType = errors.New("not a valid network strategy type")
)
var strategies = map[string]networkStrategy{
"veth": &veth{},
"loopback": &loopback{},
}
// networkStrategy represents a specific network configuration for
// a container's networking stack
type networkStrategy interface {
Create(*configs.Network, int) error
Initialize(*configs.Network) error
}
// getStrategy returns the specific network strategy for the
// provided type. If no strategy is registered for the type an
// ErrNotValidStrategyType is returned.
func getStrategy(tpe string) (networkStrategy, error) {
s, exists := strategies[tpe]
if !exists {
return nil, ErrNotValidStrategyType
}
return s, nil
}
// Returns the network statistics for the network interfaces represented by the NetworkRuntimeInfo.
func getNetworkInterfaceStats(interfaceName string) (*NetworkInterface, error) {
out := &NetworkInterface{Name: interfaceName}
// This can happen if the network runtime information is missing - possible if the
// container was created by an old version of libcontainer.
if interfaceName == "" {
return out, nil
}
type netStatsPair struct {
// Where to write the output.
Out *uint64
// The network stats file to read.
File string
}
// Ingress for host veth is from the container. Hence tx_bytes stat on the host veth is actually number of bytes received by the container.
netStats := []netStatsPair{
{Out: &out.RxBytes, File: "tx_bytes"},
{Out: &out.RxPackets, File: "tx_packets"},
{Out: &out.RxErrors, File: "tx_errors"},
{Out: &out.RxDropped, File: "tx_dropped"},
{Out: &out.TxBytes, File: "rx_bytes"},
{Out: &out.TxPackets, File: "rx_packets"},
{Out: &out.TxErrors, File: "rx_errors"},
{Out: &out.TxDropped, File: "rx_dropped"},
}
for _, netStat := range netStats {
data, err := readSysfsNetworkStats(interfaceName, netStat.File)
if err != nil {
return nil, err
}
*(netStat.Out) = data
}
return out, nil
}
// Reads the specified statistics available under /sys/class/net/<EthInterface>/statistics
func readSysfsNetworkStats(ethInterface, statsFile string) (uint64, error) {
data, err := ioutil.ReadFile(filepath.Join("/sys/class/net", ethInterface, "statistics", statsFile))
if err != nil {
return 0, err
}
return strconv.ParseUint(strings.TrimSpace(string(data)), 10, 64)
}
// loopback is a network strategy that provides a basic loopback device
type loopback struct {
}
func (l *loopback) Create(n *configs.Network, nspid int) error {
return nil
}
func (l *loopback) Initialize(config *configs.Network) error {
iface, err := net.InterfaceByName("lo")
if err != nil {
return err
}
return netlink.NetworkLinkUp(iface)
}
// veth is a network strategy that uses a bridge and creates
// a veth pair, one that stays outside on the host and the other
// is placed inside the container's namespace
type veth struct {
}
func (v *veth) Create(n *configs.Network, nspid int) error {
if n.Bridge == "" {
return fmt.Errorf("bridge is not specified")
}
bridge, err := net.InterfaceByName(n.Bridge)
if err != nil {
return err
}
if err := netlink.NetworkCreateVethPair(n.VethHost, n.VethChild, n.TxQueueLen); err != nil {
return err
}
host, err := net.InterfaceByName(n.VethHost)
if err != nil {
return err
}
if err := netlink.AddToBridge(host, bridge); err != nil {
return err
}
if err := netlink.NetworkSetMTU(host, n.Mtu); err != nil {
return err
}
if err := netlink.NetworkLinkUp(host); err != nil {
return err
}
child, err := net.InterfaceByName(n.VethChild)
if err != nil {
return err
}
return netlink.NetworkSetNsPid(child, nspid)
}
func (v *veth) Initialize(config *configs.Network) error {
vethChild := config.VethChild
if vethChild == "" {
return fmt.Errorf("vethChild is not specified")
}
child, err := net.InterfaceByName(vethChild)
if err != nil {
return err
}
if err := netlink.NetworkLinkDown(child); err != nil {
return err
}
if err := netlink.NetworkChangeName(child, defaultVethInterfaceName); err != nil {
return err
}
// get the interface again after we changed the name as the index also changes.
if child, err = net.InterfaceByName(defaultVethInterfaceName); err != nil {
return err
}
if config.MacAddress != "" {
if err := netlink.NetworkSetMacAddress(child, config.MacAddress); err != nil {
return err
}
}
ip, ipNet, err := net.ParseCIDR(config.Address)
if err != nil {
return err
}
if err := netlink.NetworkLinkAddIp(child, ip, ipNet); err != nil {
return err
}
if config.IPv6Address != "" {
if ip, ipNet, err = net.ParseCIDR(config.IPv6Address); err != nil {
return err
}
if err := netlink.NetworkLinkAddIp(child, ip, ipNet); err != nil {
return err
}
}
if err := netlink.NetworkSetMTU(child, config.Mtu); err != nil {
return err
}
if err := netlink.NetworkLinkUp(child); err != nil {
return err
}
if config.Gateway != "" {
if err := netlink.AddDefaultGw(config.Gateway, defaultVethInterfaceName); err != nil {
return err
}
}
if config.IPv6Gateway != "" {
if err := netlink.AddDefaultGw(config.IPv6Gateway, defaultVethInterfaceName); err != nil {
return err
}
}
return nil
}

View File

@ -12,7 +12,6 @@ import (
"github.com/docker/libcontainer/cgroups"
"github.com/docker/libcontainer/configs"
"github.com/docker/libcontainer/network"
"github.com/docker/libcontainer/system"
"github.com/golang/glog"
)
@ -236,7 +235,7 @@ func (p *initProcess) sendConfig() error {
func (p *initProcess) createNetworkInterfaces() error {
for _, config := range p.config.Config.Networks {
strategy, err := network.GetStrategy(config.Type)
strategy, err := getStrategy(config.Type)
if err != nil {
return err
}

View File

@ -1,25 +0,0 @@
// +build linux
package network
import (
"fmt"
"github.com/docker/libcontainer/configs"
)
// Loopback is a network strategy that provides a basic loopback device
type Loopback struct {
}
func (l *Loopback) Create(n *configs.Network, nspid int) error {
return nil
}
func (l *Loopback) Initialize(config *configs.Network) error {
// Do not set the MTU on the loopback interface - use the default.
if err := InterfaceUp("lo"); err != nil {
return fmt.Errorf("lo up %s", err)
}
return nil
}

View File

@ -1,117 +0,0 @@
// +build linux
package network
import (
"net"
"github.com/docker/libcontainer/netlink"
)
func InterfaceUp(name string) error {
iface, err := net.InterfaceByName(name)
if err != nil {
return err
}
return netlink.NetworkLinkUp(iface)
}
func InterfaceDown(name string) error {
iface, err := net.InterfaceByName(name)
if err != nil {
return err
}
return netlink.NetworkLinkDown(iface)
}
func ChangeInterfaceName(old, newName string) error {
iface, err := net.InterfaceByName(old)
if err != nil {
return err
}
return netlink.NetworkChangeName(iface, newName)
}
func CreateVethPair(name1, name2 string, txQueueLen int) error {
return netlink.NetworkCreateVethPair(name1, name2, txQueueLen)
}
func SetInterfaceInNamespacePid(name string, nsPid int) error {
iface, err := net.InterfaceByName(name)
if err != nil {
return err
}
return netlink.NetworkSetNsPid(iface, nsPid)
}
func SetInterfaceInNamespaceFd(name string, fd uintptr) error {
iface, err := net.InterfaceByName(name)
if err != nil {
return err
}
return netlink.NetworkSetNsFd(iface, int(fd))
}
func SetInterfaceMaster(name, master string) error {
iface, err := net.InterfaceByName(name)
if err != nil {
return err
}
masterIface, err := net.InterfaceByName(master)
if err != nil {
return err
}
return netlink.AddToBridge(iface, masterIface)
}
func SetDefaultGateway(ip, ifaceName string) error {
return netlink.AddDefaultGw(ip, ifaceName)
}
func SetInterfaceMac(name string, macaddr string) error {
iface, err := net.InterfaceByName(name)
if err != nil {
return err
}
return netlink.NetworkSetMacAddress(iface, macaddr)
}
func SetInterfaceIp(name string, rawIp string) error {
iface, err := net.InterfaceByName(name)
if err != nil {
return err
}
ip, ipNet, err := net.ParseCIDR(rawIp)
if err != nil {
return err
}
return netlink.NetworkLinkAddIp(iface, ip, ipNet)
}
func DeleteInterfaceIp(name string, rawIp string) error {
iface, err := net.InterfaceByName(name)
if err != nil {
return err
}
ip, ipNet, err := net.ParseCIDR(rawIp)
if err != nil {
return err
}
return netlink.NetworkLinkDelIp(iface, ip, ipNet)
}
func SetMtu(name string, mtu int) error {
iface, err := net.InterfaceByName(name)
if err != nil {
return err
}
return netlink.NetworkSetMTU(iface, mtu)
}
func SetHairpinMode(name string, enabled bool) error {
iface, err := net.InterfaceByName(name)
if err != nil {
return err
}
return netlink.SetHairpinMode(iface, enabled)
}

View File

@ -1,70 +0,0 @@
package network
import (
"io/ioutil"
"path/filepath"
"strconv"
"strings"
)
type NetworkStats struct {
RxBytes uint64 `json:"rx_bytes"`
RxPackets uint64 `json:"rx_packets"`
RxErrors uint64 `json:"rx_errors"`
RxDropped uint64 `json:"rx_dropped"`
TxBytes uint64 `json:"tx_bytes"`
TxPackets uint64 `json:"tx_packets"`
TxErrors uint64 `json:"tx_errors"`
TxDropped uint64 `json:"tx_dropped"`
}
// Returns the network statistics for the network interfaces represented by the NetworkRuntimeInfo.
func GetStats(vethHostInterface string) (*NetworkStats, error) {
// This can happen if the network runtime information is missing - possible if the container was created by an old version of libcontainer.
if vethHostInterface == "" {
return &NetworkStats{}, nil
}
out := &NetworkStats{}
type netStatsPair struct {
// Where to write the output.
Out *uint64
// The network stats file to read.
File string
}
// Ingress for host veth is from the container. Hence tx_bytes stat on the host veth is actually number of bytes received by the container.
netStats := []netStatsPair{
{Out: &out.RxBytes, File: "tx_bytes"},
{Out: &out.RxPackets, File: "tx_packets"},
{Out: &out.RxErrors, File: "tx_errors"},
{Out: &out.RxDropped, File: "tx_dropped"},
{Out: &out.TxBytes, File: "rx_bytes"},
{Out: &out.TxPackets, File: "rx_packets"},
{Out: &out.TxErrors, File: "rx_errors"},
{Out: &out.TxDropped, File: "rx_dropped"},
}
for _, netStat := range netStats {
data, err := readSysfsNetworkStats(vethHostInterface, netStat.File)
if err != nil {
return nil, err
}
*(netStat.Out) = data
}
return out, nil
}
// Reads the specified statistics available under /sys/class/net/<EthInterface>/statistics
func readSysfsNetworkStats(ethInterface, statsFile string) (uint64, error) {
fullPath := filepath.Join("/sys/class/net", ethInterface, "statistics", statsFile)
data, err := ioutil.ReadFile(fullPath)
if err != nil {
return 0, err
}
value, err := strconv.ParseUint(strings.TrimSpace(string(data)), 10, 64)
if err != nil {
return 0, err
}
return value, err
}

View File

@ -1,36 +0,0 @@
// +build linux
package network
import (
"errors"
"github.com/docker/libcontainer/configs"
)
var (
ErrNotValidStrategyType = errors.New("not a valid network strategy type")
)
var strategies = map[string]NetworkStrategy{
"veth": &Veth{},
"loopback": &Loopback{},
}
// NetworkStrategy represents a specific network configuration for
// a container's networking stack
type NetworkStrategy interface {
Create(*configs.Network, int) error
Initialize(*configs.Network) error
}
// GetStrategy returns the specific network strategy for the
// provided type. If no strategy is registered for the type an
// ErrNotValidStrategyType is returned.
func GetStrategy(tpe string) (NetworkStrategy, error) {
s, exists := strategies[tpe]
if !exists {
return nil, ErrNotValidStrategyType
}
return s, nil
}

View File

@ -1,85 +0,0 @@
// +build linux
package network
import (
"fmt"
"github.com/docker/libcontainer/configs"
)
// Veth is a network strategy that uses a bridge and creates
// a veth pair, one that stays outside on the host and the other
// is placed inside the container's namespace
type Veth struct {
}
const defaultDevice = "eth0"
func (v *Veth) Create(n *configs.Network, nspid int) error {
var (
bridge = n.Bridge
txQueueLen = n.TxQueueLen
)
if bridge == "" {
return fmt.Errorf("bridge is not specified")
}
if err := CreateVethPair(n.VethHost, n.VethChild, txQueueLen); err != nil {
return err
}
if err := SetInterfaceMaster(n.VethHost, bridge); err != nil {
return err
}
if err := SetMtu(n.VethHost, n.Mtu); err != nil {
return err
}
if err := InterfaceUp(n.VethHost); err != nil {
return err
}
return SetInterfaceInNamespacePid(n.VethChild, nspid)
return nil
}
func (v *Veth) Initialize(config *configs.Network) error {
vethChild := config.VethChild
if vethChild == "" {
return fmt.Errorf("vethChild is not specified")
}
if err := InterfaceDown(vethChild); err != nil {
return fmt.Errorf("interface down %s %s", vethChild, err)
}
if err := ChangeInterfaceName(vethChild, defaultDevice); err != nil {
return fmt.Errorf("change %s to %s %s", vethChild, defaultDevice, err)
}
if config.MacAddress != "" {
if err := SetInterfaceMac(defaultDevice, config.MacAddress); err != nil {
return fmt.Errorf("set %s mac %s", defaultDevice, err)
}
}
if err := SetInterfaceIp(defaultDevice, config.Address); err != nil {
return fmt.Errorf("set %s ip %s", defaultDevice, err)
}
if config.IPv6Address != "" {
if err := SetInterfaceIp(defaultDevice, config.IPv6Address); err != nil {
return fmt.Errorf("set %s ipv6 %s", defaultDevice, err)
}
}
if err := SetMtu(defaultDevice, config.Mtu); err != nil {
return fmt.Errorf("set %s mtu to %d %s", defaultDevice, config.Mtu, err)
}
if err := InterfaceUp(defaultDevice); err != nil {
return fmt.Errorf("%s up %s", defaultDevice, err)
}
if config.Gateway != "" {
if err := SetDefaultGateway(config.Gateway, defaultDevice); err != nil {
return fmt.Errorf("set gateway to %s on device %s failed with %s", config.Gateway, defaultDevice, err)
}
}
if config.IPv6Gateway != "" {
if err := SetDefaultGateway(config.IPv6Gateway, defaultDevice); err != nil {
return fmt.Errorf("set gateway for ipv6 to %s on device %s failed with %s", config.IPv6Gateway, defaultDevice, err)
}
}
return nil
}

22
stats.go Normal file
View File

@ -0,0 +1,22 @@
package libcontainer
import "github.com/docker/libcontainer/cgroups"
type NetworkInterface struct {
// Name is the name of the network interface.
Name string
RxBytes uint64
RxPackets uint64
RxErrors uint64
RxDropped uint64
TxBytes uint64
TxPackets uint64
TxErrors uint64
TxDropped uint64
}
type Stats struct {
Interfaces []*NetworkInterface
CgroupStats *cgroups.Stats
}