Преглед изворни кода

Merge pull request #4441 from crosbymichael/add-net-flag

Add --net flag to docker run and allow host network stack
Guillaume J. Charmes пре 11 година
родитељ
комит
70fef1460a

+ 91 - 14
daemon/container.go

@@ -325,7 +325,7 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s
 	})
 	})
 }
 }
 
 
-func populateCommand(c *Container, env []string) {
+func populateCommand(c *Container, env []string) error {
 	var (
 	var (
 		en      *execdriver.Network
 		en      *execdriver.Network
 		context = make(map[string][]string)
 		context = make(map[string][]string)
@@ -338,14 +338,29 @@ func populateCommand(c *Container, env []string) {
 		Interface: nil,
 		Interface: nil,
 	}
 	}
 
 
-	if !c.Config.NetworkDisabled {
-		network := c.NetworkSettings
-		en.Interface = &execdriver.NetworkInterface{
-			Gateway:     network.Gateway,
-			Bridge:      network.Bridge,
-			IPAddress:   network.IPAddress,
-			IPPrefixLen: network.IPPrefixLen,
+	parts := strings.SplitN(string(c.hostConfig.NetworkMode), ":", 2)
+	switch parts[0] {
+	case "none":
+	case "host":
+		en.HostNetworking = true
+	case "bridge", "": // empty string to support existing containers
+		if !c.Config.NetworkDisabled {
+			network := c.NetworkSettings
+			en.Interface = &execdriver.NetworkInterface{
+				Gateway:     network.Gateway,
+				Bridge:      network.Bridge,
+				IPAddress:   network.IPAddress,
+				IPPrefixLen: network.IPPrefixLen,
+			}
 		}
 		}
+	case "container":
+		nc, err := c.getNetworkedContainer()
+		if err != nil {
+			return err
+		}
+		en.ContainerID = nc.ID
+	default:
+		return fmt.Errorf("invalid network mode: %s", c.hostConfig.NetworkMode)
 	}
 	}
 
 
 	// TODO: this can be removed after lxc-conf is fully deprecated
 	// TODO: this can be removed after lxc-conf is fully deprecated
@@ -372,6 +387,7 @@ func populateCommand(c *Container, env []string) {
 	}
 	}
 	c.command.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
 	c.command.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
 	c.command.Env = env
 	c.command.Env = env
+	return nil
 }
 }
 
 
 func (container *Container) Start() (err error) {
 func (container *Container) Start() (err error) {
@@ -415,7 +431,9 @@ func (container *Container) Start() (err error) {
 	if err := container.setupWorkingDirectory(); err != nil {
 	if err := container.setupWorkingDirectory(); err != nil {
 		return err
 		return err
 	}
 	}
-	populateCommand(container, env)
+	if err := populateCommand(container, env); err != nil {
+		return err
+	}
 	if err := setupMountsForContainer(container); err != nil {
 	if err := setupMountsForContainer(container); err != nil {
 		return err
 		return err
 	}
 	}
@@ -485,9 +503,18 @@ func (container *Container) StderrLogPipe() io.ReadCloser {
 	return utils.NewBufReader(reader)
 	return utils.NewBufReader(reader)
 }
 }
 
 
-func (container *Container) buildHostnameAndHostsFiles(IP string) {
+func (container *Container) buildHostname() {
 	container.HostnamePath = path.Join(container.root, "hostname")
 	container.HostnamePath = path.Join(container.root, "hostname")
-	ioutil.WriteFile(container.HostnamePath, []byte(container.Config.Hostname+"\n"), 0644)
+
+	if container.Config.Domainname != "" {
+		ioutil.WriteFile(container.HostnamePath, []byte(fmt.Sprintf("%s.%s\n", container.Config.Hostname, container.Config.Domainname)), 0644)
+	} else {
+		ioutil.WriteFile(container.HostnamePath, []byte(container.Config.Hostname+"\n"), 0644)
+	}
+}
+
+func (container *Container) buildHostnameAndHostsFiles(IP string) {
+	container.buildHostname()
 
 
 	hostsContent := []byte(`
 	hostsContent := []byte(`
 127.0.0.1	localhost
 127.0.0.1	localhost
@@ -505,12 +532,12 @@ ff02::2		ip6-allrouters
 	} else if !container.Config.NetworkDisabled {
 	} else if !container.Config.NetworkDisabled {
 		hostsContent = append([]byte(fmt.Sprintf("%s\t%s\n", IP, container.Config.Hostname)), hostsContent...)
 		hostsContent = append([]byte(fmt.Sprintf("%s\t%s\n", IP, container.Config.Hostname)), hostsContent...)
 	}
 	}
-
 	ioutil.WriteFile(container.HostsPath, hostsContent, 0644)
 	ioutil.WriteFile(container.HostsPath, hostsContent, 0644)
 }
 }
 
 
 func (container *Container) allocateNetwork() error {
 func (container *Container) allocateNetwork() error {
-	if container.Config.NetworkDisabled {
+	mode := container.hostConfig.NetworkMode
+	if container.Config.NetworkDisabled || mode.IsContainer() || mode.IsHost() {
 		return nil
 		return nil
 	}
 	}
 
 
@@ -963,14 +990,22 @@ func (container *Container) setupContainerDns() error {
 	if container.ResolvConfPath != "" {
 	if container.ResolvConfPath != "" {
 		return nil
 		return nil
 	}
 	}
+
 	var (
 	var (
 		config = container.hostConfig
 		config = container.hostConfig
 		daemon = container.daemon
 		daemon = container.daemon
 	)
 	)
+
+	if config.NetworkMode == "host" {
+		container.ResolvConfPath = "/etc/resolv.conf"
+		return nil
+	}
+
 	resolvConf, err := utils.GetResolvConf()
 	resolvConf, err := utils.GetResolvConf()
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
+
 	// If custom dns exists, then create a resolv.conf for the container
 	// If custom dns exists, then create a resolv.conf for the container
 	if len(config.Dns) > 0 || len(daemon.config.Dns) > 0 || len(config.DnsSearch) > 0 || len(daemon.config.DnsSearch) > 0 {
 	if len(config.Dns) > 0 || len(daemon.config.Dns) > 0 || len(config.DnsSearch) > 0 || len(daemon.config.DnsSearch) > 0 {
 		var (
 		var (
@@ -1010,7 +1045,32 @@ func (container *Container) setupContainerDns() error {
 }
 }
 
 
 func (container *Container) initializeNetworking() error {
 func (container *Container) initializeNetworking() error {
-	if container.daemon.config.DisableNetwork {
+	var err error
+	if container.hostConfig.NetworkMode.IsHost() {
+		container.Config.Hostname, err = os.Hostname()
+		if err != nil {
+			return err
+		}
+
+		parts := strings.SplitN(container.Config.Hostname, ".", 2)
+		if len(parts) > 1 {
+			container.Config.Hostname = parts[0]
+			container.Config.Domainname = parts[1]
+		}
+		container.HostsPath = "/etc/hosts"
+
+		container.buildHostname()
+	} else if container.hostConfig.NetworkMode.IsContainer() {
+		// we need to get the hosts files from the container to join
+		nc, err := container.getNetworkedContainer()
+		if err != nil {
+			return err
+		}
+		container.HostsPath = nc.HostsPath
+		container.ResolvConfPath = nc.ResolvConfPath
+		container.Config.Hostname = nc.Config.Hostname
+		container.Config.Domainname = nc.Config.Domainname
+	} else if container.daemon.config.DisableNetwork {
 		container.Config.NetworkDisabled = true
 		container.Config.NetworkDisabled = true
 		container.buildHostnameAndHostsFiles("127.0.1.1")
 		container.buildHostnameAndHostsFiles("127.0.1.1")
 	} else {
 	} else {
@@ -1219,3 +1279,20 @@ func (container *Container) GetMountLabel() string {
 	}
 	}
 	return container.MountLabel
 	return container.MountLabel
 }
 }
+
+func (container *Container) getNetworkedContainer() (*Container, error) {
+	parts := strings.SplitN(string(container.hostConfig.NetworkMode), ":", 2)
+	switch parts[0] {
+	case "container":
+		nc := container.daemon.Get(parts[1])
+		if nc == nil {
+			return nil, fmt.Errorf("no such container to join network: %s", parts[1])
+		}
+		if !nc.State.IsRunning() {
+			return nil, fmt.Errorf("cannot join network of a non running container: %s", parts[1])
+		}
+		return nc, nil
+	default:
+		return nil, fmt.Errorf("network mode not set to container")
+	}
+}

+ 4 - 2
daemon/execdriver/driver.go

@@ -89,8 +89,10 @@ type Driver interface {
 
 
 // Network settings of the container
 // Network settings of the container
 type Network struct {
 type Network struct {
-	Interface *NetworkInterface `json:"interface"` // if interface is nil then networking is disabled
-	Mtu       int               `json:"mtu"`
+	Interface      *NetworkInterface `json:"interface"` // if interface is nil then networking is disabled
+	Mtu            int               `json:"mtu"`
+	ContainerID    string            `json:"container_id"` // id of the container to join network.
+	HostNetworking bool              `json:"host_networking"`
 }
 }
 
 
 type NetworkInterface struct {
 type NetworkInterface struct {

+ 5 - 4
daemon/execdriver/lxc/init.go

@@ -3,15 +3,16 @@ package lxc
 import (
 import (
 	"encoding/json"
 	"encoding/json"
 	"fmt"
 	"fmt"
-	"github.com/dotcloud/docker/daemon/execdriver"
-	"github.com/dotcloud/docker/pkg/netlink"
-	"github.com/dotcloud/docker/pkg/user"
-	"github.com/syndtr/gocapability/capability"
 	"io/ioutil"
 	"io/ioutil"
 	"net"
 	"net"
 	"os"
 	"os"
 	"strings"
 	"strings"
 	"syscall"
 	"syscall"
+
+	"github.com/dotcloud/docker/daemon/execdriver"
+	"github.com/dotcloud/docker/pkg/netlink"
+	"github.com/dotcloud/docker/pkg/user"
+	"github.com/syndtr/gocapability/capability"
 )
 )
 
 
 // Clear environment pollution introduced by lxc-start
 // Clear environment pollution introduced by lxc-start

+ 3 - 2
daemon/execdriver/lxc/lxc_template.go

@@ -14,12 +14,13 @@ const LxcTemplate = `
 lxc.network.type = veth
 lxc.network.type = veth
 lxc.network.link = {{.Network.Interface.Bridge}}
 lxc.network.link = {{.Network.Interface.Bridge}}
 lxc.network.name = eth0
 lxc.network.name = eth0
-{{else}}
+lxc.network.mtu = {{.Network.Mtu}}
+{{else if not .Network.HostNetworking}}
 # network is disabled (-n=false)
 # network is disabled (-n=false)
 lxc.network.type = empty
 lxc.network.type = empty
 lxc.network.flags = up
 lxc.network.flags = up
-{{end}}
 lxc.network.mtu = {{.Network.Mtu}}
 lxc.network.mtu = {{.Network.Mtu}}
+{{end}}
 
 
 # root filesystem
 # root filesystem
 {{$ROOTFS := .Rootfs}}
 {{$ROOTFS := .Rootfs}}

+ 19 - 0
daemon/execdriver/native/create.go

@@ -3,6 +3,7 @@ package native
 import (
 import (
 	"fmt"
 	"fmt"
 	"os"
 	"os"
+	"path/filepath"
 
 
 	"github.com/dotcloud/docker/daemon/execdriver"
 	"github.com/dotcloud/docker/daemon/execdriver"
 	"github.com/dotcloud/docker/daemon/execdriver/native/configuration"
 	"github.com/dotcloud/docker/daemon/execdriver/native/configuration"
@@ -52,6 +53,10 @@ func (d *driver) createContainer(c *execdriver.Command) (*libcontainer.Container
 }
 }
 
 
 func (d *driver) createNetwork(container *libcontainer.Container, c *execdriver.Command) error {
 func (d *driver) createNetwork(container *libcontainer.Container, c *execdriver.Command) error {
+	if c.Network.HostNetworking {
+		container.Namespaces.Get("NEWNET").Enabled = false
+		return nil
+	}
 	container.Networks = []*libcontainer.Network{
 	container.Networks = []*libcontainer.Network{
 		{
 		{
 			Mtu:     c.Network.Mtu,
 			Mtu:     c.Network.Mtu,
@@ -75,6 +80,20 @@ func (d *driver) createNetwork(container *libcontainer.Container, c *execdriver.
 		}
 		}
 		container.Networks = append(container.Networks, &vethNetwork)
 		container.Networks = append(container.Networks, &vethNetwork)
 	}
 	}
+
+	if c.Network.ContainerID != "" {
+		cmd := d.activeContainers[c.Network.ContainerID]
+		if cmd == nil || cmd.Process == nil {
+			return fmt.Errorf("%s is not a valid running container to join", c.Network.ContainerID)
+		}
+		nspath := filepath.Join("/proc", fmt.Sprint(cmd.Process.Pid), "ns", "net")
+		container.Networks = append(container.Networks, &libcontainer.Network{
+			Type: "netns",
+			Context: libcontainer.Context{
+				"nspath": nspath,
+			},
+		})
+	}
 	return nil
 	return nil
 }
 }
 
 

+ 44 - 2
docs/sources/reference/run.md

@@ -136,8 +136,8 @@ PID files):
 
 
 ## Network Settings
 ## Network Settings
 
 
-    -n=true   : Enable networking for this container
-    --dns=[]  : Set custom dns servers for the container
+    --dns=[]     : Set custom dns servers for the container
+    --net=bridge : Set the network mode
 
 
 By default, all containers have networking enabled and they can make any
 By default, all containers have networking enabled and they can make any
 outgoing connections. The operator can completely disable networking
 outgoing connections. The operator can completely disable networking
@@ -148,6 +148,48 @@ files or STDIN/STDOUT only.
 Your container will use the same DNS servers as the host by default, but
 Your container will use the same DNS servers as the host by default, but
 you can override this with `--dns`.
 you can override this with `--dns`.
 
 
+Supported networking modes are: 
+
+* none - no networking in the container
+* bridge - (default) connect the container to the bridge via veth interfaces
+* host - use the host's network stack inside the container
+* container - use another container's network stack
+
+#### Mode: none
+With the networking mode set to `none` a container will not have a access to 
+any external routes.  The container will still have a `loopback` interface 
+enabled in the container but it does not have any routes to external traffic.
+
+#### Mode: bridge
+With the networking mode set to `bridge` a container will use docker's default
+networking setup.  A bridge is setup on the host, commonly named `docker0`, 
+and a pair of veth interfaces will be created for the container.  One side of 
+the veth pair will remain on the host attached to the bridge while the other 
+side of the pair will be placed inside the container's namespaces in addition 
+to the `loopback` interface.  An IP address will be allocated for containers 
+on the bridge's network and trafic will be routed though this bridge to the
+container.
+
+#### Mode: host
+With the networking mode set to `host` a container will share the host's
+network stack and all interfaces from the host will be available to the 
+container.  The container's hostname will match the hostname on the host 
+system.  Publishing ports and linking to other containers will not work 
+when sharing the host's network stack.  
+
+#### Mode: container
+With the networking mode set to `container` a container will share the 
+network stack of another container.  The other container's name must be 
+provided in the format of `--net container:<name|id>`.
+
+Example running a redis container with redis binding to localhost then 
+running the redis-cli and connecting to the redis server over the 
+localhost interface.
+
+    $ docker run -d --name redis example/redis --bind 127.0.0.1
+    $ # use the redis container's network stack to access localhost
+    $ docker run --rm -ti --net container:redis example/redis-cli -h 127.0.0.1
+
 ## Clean Up (–rm)
 ## Clean Up (–rm)
 
 
 By default a container's file system persists even after the container
 By default a container's file system persists even after the container

+ 15 - 0
runconfig/hostconfig.go

@@ -1,11 +1,24 @@
 package runconfig
 package runconfig
 
 
 import (
 import (
+	"strings"
+
 	"github.com/dotcloud/docker/engine"
 	"github.com/dotcloud/docker/engine"
 	"github.com/dotcloud/docker/nat"
 	"github.com/dotcloud/docker/nat"
 	"github.com/dotcloud/docker/utils"
 	"github.com/dotcloud/docker/utils"
 )
 )
 
 
+type NetworkMode string
+
+func (n NetworkMode) IsHost() bool {
+	return n == "host"
+}
+
+func (n NetworkMode) IsContainer() bool {
+	parts := strings.SplitN(string(n), ":", 2)
+	return len(parts) > 1 && parts[0] == "container"
+}
+
 type HostConfig struct {
 type HostConfig struct {
 	Binds           []string
 	Binds           []string
 	ContainerIDFile string
 	ContainerIDFile string
@@ -17,6 +30,7 @@ type HostConfig struct {
 	Dns             []string
 	Dns             []string
 	DnsSearch       []string
 	DnsSearch       []string
 	VolumesFrom     []string
 	VolumesFrom     []string
+	NetworkMode     NetworkMode
 }
 }
 
 
 func ContainerHostConfigFromJob(job *engine.Job) *HostConfig {
 func ContainerHostConfigFromJob(job *engine.Job) *HostConfig {
@@ -24,6 +38,7 @@ func ContainerHostConfigFromJob(job *engine.Job) *HostConfig {
 		ContainerIDFile: job.Getenv("ContainerIDFile"),
 		ContainerIDFile: job.Getenv("ContainerIDFile"),
 		Privileged:      job.GetenvBool("Privileged"),
 		Privileged:      job.GetenvBool("Privileged"),
 		PublishAllPorts: job.GetenvBool("PublishAllPorts"),
 		PublishAllPorts: job.GetenvBool("PublishAllPorts"),
+		NetworkMode:     NetworkMode(job.Getenv("NetworkMode")),
 	}
 	}
 	job.GetenvJson("LxcConf", &hostConfig.LxcConf)
 	job.GetenvJson("LxcConf", &hostConfig.LxcConf)
 	job.GetenvJson("PortBindings", &hostConfig.PortBindings)
 	job.GetenvJson("PortBindings", &hostConfig.PortBindings)

+ 26 - 5
runconfig/parse.go

@@ -2,14 +2,15 @@ package runconfig
 
 
 import (
 import (
 	"fmt"
 	"fmt"
+	"io/ioutil"
+	"path"
+	"strings"
+
 	"github.com/dotcloud/docker/nat"
 	"github.com/dotcloud/docker/nat"
 	"github.com/dotcloud/docker/opts"
 	"github.com/dotcloud/docker/opts"
 	flag "github.com/dotcloud/docker/pkg/mflag"
 	flag "github.com/dotcloud/docker/pkg/mflag"
 	"github.com/dotcloud/docker/pkg/sysinfo"
 	"github.com/dotcloud/docker/pkg/sysinfo"
 	"github.com/dotcloud/docker/utils"
 	"github.com/dotcloud/docker/utils"
-	"io/ioutil"
-	"path"
-	"strings"
 )
 )
 
 
 var (
 var (
@@ -49,7 +50,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf
 
 
 		flAutoRemove      = cmd.Bool([]string{"#rm", "-rm"}, false, "Automatically remove the container when it exits (incompatible with -d)")
 		flAutoRemove      = cmd.Bool([]string{"#rm", "-rm"}, false, "Automatically remove the container when it exits (incompatible with -d)")
 		flDetach          = cmd.Bool([]string{"d", "-detach"}, false, "Detached mode: Run container in the background, print new container id")
 		flDetach          = cmd.Bool([]string{"d", "-detach"}, false, "Detached mode: Run container in the background, print new container id")
-		flNetwork         = cmd.Bool([]string{"n", "-networking"}, true, "Enable networking for this container")
+		flNetwork         = cmd.Bool([]string{"#n", "#-networking"}, true, "Enable networking for this container")
 		flPrivileged      = cmd.Bool([]string{"#privileged", "-privileged"}, false, "Give extended privileges to this container")
 		flPrivileged      = cmd.Bool([]string{"#privileged", "-privileged"}, false, "Give extended privileges to this container")
 		flPublishAll      = cmd.Bool([]string{"P", "-publish-all"}, false, "Publish all exposed ports to the host interfaces")
 		flPublishAll      = cmd.Bool([]string{"P", "-publish-all"}, false, "Publish all exposed ports to the host interfaces")
 		flStdin           = cmd.Bool([]string{"i", "-interactive"}, false, "Keep stdin open even if not attached")
 		flStdin           = cmd.Bool([]string{"i", "-interactive"}, false, "Keep stdin open even if not attached")
@@ -61,7 +62,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf
 		flUser            = cmd.String([]string{"u", "-user"}, "", "Username or UID")
 		flUser            = cmd.String([]string{"u", "-user"}, "", "Username or UID")
 		flWorkingDir      = cmd.String([]string{"w", "-workdir"}, "", "Working directory inside the container")
 		flWorkingDir      = cmd.String([]string{"w", "-workdir"}, "", "Working directory inside the container")
 		flCpuShares       = cmd.Int64([]string{"c", "-cpu-shares"}, 0, "CPU shares (relative weight)")
 		flCpuShares       = cmd.Int64([]string{"c", "-cpu-shares"}, 0, "CPU shares (relative weight)")
-
+		flNetMode         = cmd.String([]string{"-net"}, "bridge", "Set the Network mode for the container ('bridge': creates a new network stack for the container on the docker bridge, 'none': no networking for this container, 'container:<name|id>': reuses another container network stack)")
 		// For documentation purpose
 		// For documentation purpose
 		_ = cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxify all received signal to the process (even in non-tty mode)")
 		_ = cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxify all received signal to the process (even in non-tty mode)")
 		_ = cmd.String([]string{"#name", "-name"}, "", "Assign a name to the container")
 		_ = cmd.String([]string{"#name", "-name"}, "", "Assign a name to the container")
@@ -197,6 +198,11 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf
 	// boo, there's no debug output for docker run
 	// boo, there's no debug output for docker run
 	//utils.Debugf("Environment variables for the container: %#v", envVariables)
 	//utils.Debugf("Environment variables for the container: %#v", envVariables)
 
 
+	netMode, err := parseNetMode(*flNetMode)
+	if err != nil {
+		return nil, nil, cmd, fmt.Errorf("--net: invalid net mode: %v", err)
+	}
+
 	config := &Config{
 	config := &Config{
 		Hostname:        hostname,
 		Hostname:        hostname,
 		Domainname:      domainname,
 		Domainname:      domainname,
@@ -230,6 +236,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf
 		Dns:             flDns.GetAll(),
 		Dns:             flDns.GetAll(),
 		DnsSearch:       flDnsSearch.GetAll(),
 		DnsSearch:       flDnsSearch.GetAll(),
 		VolumesFrom:     flVolumesFrom.GetAll(),
 		VolumesFrom:     flVolumesFrom.GetAll(),
+		NetworkMode:     netMode,
 	}
 	}
 
 
 	if sysInfo != nil && flMemory > 0 && !sysInfo.SwapLimit {
 	if sysInfo != nil && flMemory > 0 && !sysInfo.SwapLimit {
@@ -274,3 +281,17 @@ func parseKeyValueOpts(opts opts.ListOpts) ([]utils.KeyValuePair, error) {
 	}
 	}
 	return out, nil
 	return out, nil
 }
 }
+
+func parseNetMode(netMode string) (NetworkMode, error) {
+	parts := strings.Split(netMode, ":")
+	switch mode := parts[0]; mode {
+	case "bridge", "none", "host":
+	case "container":
+		if len(parts) < 2 || parts[1] == "" {
+			return "", fmt.Errorf("invalid container format container:<name|id>")
+		}
+	default:
+		return "", fmt.Errorf("invalid --net: %s", netMode)
+	}
+	return NetworkMode(netMode), nil
+}

+ 2 - 1
runconfig/parse_test.go

@@ -1,8 +1,9 @@
 package runconfig
 package runconfig
 
 
 import (
 import (
-	"github.com/dotcloud/docker/utils"
 	"testing"
 	"testing"
+
+	"github.com/dotcloud/docker/utils"
 )
 )
 
 
 func TestParseLxcConfOpt(t *testing.T) {
 func TestParseLxcConfOpt(t *testing.T) {