The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/6753
This e-mail was sent by the LXC bot, direct replies will not reach the author unless they happen to be subscribed to this list. === Description (from pull-request) ===
From 2e4484146b2d6ad039439f4e648dc50fe6c3079b Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 22 Jan 2020 18:15:05 +0000 Subject: [PATCH 1/4] lxd/device: Relaxes requirement for name property when not using containers Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/device/infiniband_physical.go | 2 +- lxd/device/infiniband_sriov.go | 2 +- lxd/device/nic_bridged.go | 2 +- lxd/device/nic_ipvlan.go | 2 +- lxd/device/nic_macvlan.go | 3 ++- lxd/device/nic_p2p.go | 2 +- lxd/device/nic_physical.go | 2 +- lxd/device/nic_routed.go | 2 +- lxd/device/nic_sriov.go | 2 +- 9 files changed, 10 insertions(+), 9 deletions(-) diff --git a/lxd/device/infiniband_physical.go b/lxd/device/infiniband_physical.go index f1ba582621..6c6947b5bc 100644 --- a/lxd/device/infiniband_physical.go +++ b/lxd/device/infiniband_physical.go @@ -45,7 +45,7 @@ func (d *infinibandPhysical) validateConfig() error { // validateEnvironment checks the runtime environment for correctness. func (d *infinibandPhysical) validateEnvironment() error { - if d.config["name"] == "" { + if d.inst.Type() == instancetype.Container && d.config["name"] == "" { return fmt.Errorf("Requires name property to start") } diff --git a/lxd/device/infiniband_sriov.go b/lxd/device/infiniband_sriov.go index ea37fda93c..cb673e5ee5 100644 --- a/lxd/device/infiniband_sriov.go +++ b/lxd/device/infiniband_sriov.go @@ -46,7 +46,7 @@ func (d *infinibandSRIOV) validateConfig() error { // validateEnvironment checks the runtime environment for correctness. func (d *infinibandSRIOV) validateEnvironment() error { - if d.config["name"] == "" { + if d.inst.Type() == instancetype.Container && d.config["name"] == "" { return fmt.Errorf("Requires name property to start") } diff --git a/lxd/device/nic_bridged.go b/lxd/device/nic_bridged.go index 6359052328..dd1e534799 100644 --- a/lxd/device/nic_bridged.go +++ b/lxd/device/nic_bridged.go @@ -83,7 +83,7 @@ func (d *nicBridged) validateConfig() error { // validateEnvironment checks the runtime environment for correctness. func (d *nicBridged) validateEnvironment() error { - if d.config["name"] == "" { + if d.inst.Type() == instancetype.Container && d.config["name"] == "" { return fmt.Errorf("Requires name property to start") } diff --git a/lxd/device/nic_ipvlan.go b/lxd/device/nic_ipvlan.go index 480b8af1df..ee61a8353e 100644 --- a/lxd/device/nic_ipvlan.go +++ b/lxd/device/nic_ipvlan.go @@ -58,7 +58,7 @@ func (d *nicIPVLAN) validateConfig() error { // validateEnvironment checks the runtime environment for correctness. func (d *nicIPVLAN) validateEnvironment() error { - if d.config["name"] == "" { + if d.inst.Type() == instancetype.Container && d.config["name"] == "" { return fmt.Errorf("Requires name property to start") } diff --git a/lxd/device/nic_macvlan.go b/lxd/device/nic_macvlan.go index 5471bc869b..45df01b8d2 100644 --- a/lxd/device/nic_macvlan.go +++ b/lxd/device/nic_macvlan.go @@ -5,6 +5,7 @@ import ( deviceConfig "github.com/lxc/lxd/lxd/device/config" "github.com/lxc/lxd/lxd/instance/instancetype" + "github.com/lxc/lxd/lxd/revert" "github.com/lxc/lxd/shared" ) @@ -30,7 +31,7 @@ func (d *nicMACVLAN) validateConfig() error { // validateEnvironment checks the runtime environment for correctness. func (d *nicMACVLAN) validateEnvironment() error { - if d.config["name"] == "" { + if d.inst.Type() == instancetype.Container && d.config["name"] == "" { return fmt.Errorf("Requires name property to start") } diff --git a/lxd/device/nic_p2p.go b/lxd/device/nic_p2p.go index 038cb420a2..0bfa97462f 100644 --- a/lxd/device/nic_p2p.go +++ b/lxd/device/nic_p2p.go @@ -40,7 +40,7 @@ func (d *nicP2P) validateConfig() error { // validateEnvironment checks the runtime environment for correctness. func (d *nicP2P) validateEnvironment() error { - if d.config["name"] == "" { + if d.inst.Type() == instancetype.Container && d.config["name"] == "" { return fmt.Errorf("Requires name property to start") } diff --git a/lxd/device/nic_physical.go b/lxd/device/nic_physical.go index 09f4002568..54d98c6a0d 100644 --- a/lxd/device/nic_physical.go +++ b/lxd/device/nic_physical.go @@ -38,7 +38,7 @@ func (d *nicPhysical) validateConfig() error { // validateEnvironment checks the runtime environment for correctness. func (d *nicPhysical) validateEnvironment() error { - if d.config["name"] == "" { + if d.inst.Type() == instancetype.Container && d.config["name"] == "" { return fmt.Errorf("Requires name property to start") } diff --git a/lxd/device/nic_routed.go b/lxd/device/nic_routed.go index 8b9db5eb31..3f6d0cb66f 100644 --- a/lxd/device/nic_routed.go +++ b/lxd/device/nic_routed.go @@ -63,7 +63,7 @@ func (d *nicRouted) validateConfig() error { // validateEnvironment checks the runtime environment for correctness. func (d *nicRouted) validateEnvironment() error { - if d.config["name"] == "" { + if d.inst.Type() == instancetype.Container && d.config["name"] == "" { return fmt.Errorf("Requires name property to start") } diff --git a/lxd/device/nic_sriov.go b/lxd/device/nic_sriov.go index 7c426e01e5..b9fb4a8f30 100644 --- a/lxd/device/nic_sriov.go +++ b/lxd/device/nic_sriov.go @@ -49,7 +49,7 @@ func (d *nicSRIOV) validateConfig() error { // validateEnvironment checks the runtime environment for correctness. func (d *nicSRIOV) validateEnvironment() error { - if d.config["name"] == "" { + if d.inst.Type() == instancetype.Container && d.config["name"] == "" { return fmt.Errorf("Requires name property to start") } From 254921475b1d9837cb1ea20e756131df1c2285b7 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 22 Jan 2020 18:15:58 +0000 Subject: [PATCH 2/4] lxd/device/nic/macvlan: Clean up valid fields Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/device/nic_macvlan.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lxd/device/nic_macvlan.go b/lxd/device/nic_macvlan.go index 45df01b8d2..e1a9780748 100644 --- a/lxd/device/nic_macvlan.go +++ b/lxd/device/nic_macvlan.go @@ -20,7 +20,15 @@ func (d *nicMACVLAN) validateConfig() error { } requiredFields := []string{"parent"} - optionalFields := []string{"name", "mtu", "hwaddr", "vlan", "maas.subnet.ipv4", "maas.subnet.ipv6", "boot.priority"} + optionalFields := []string{ + "name", + "mtu", + "hwaddr", + "vlan", + "maas.subnet.ipv4", + "maas.subnet.ipv6", + "boot.priority", + } err := d.config.Validate(nicValidationRules(requiredFields, optionalFields)) if err != nil { return err From 860005ce080d1bf0167d36a7f462242103d32fa3 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 22 Jan 2020 18:17:55 +0000 Subject: [PATCH 3/4] lxd/device/nic/macvlan: Adds VM support and improves revert Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/device/nic_macvlan.go | 52 +++++++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/lxd/device/nic_macvlan.go b/lxd/device/nic_macvlan.go index e1a9780748..43cb93a651 100644 --- a/lxd/device/nic_macvlan.go +++ b/lxd/device/nic_macvlan.go @@ -15,7 +15,7 @@ type nicMACVLAN struct { // validateConfig checks the supplied config for correctness. func (d *nicMACVLAN) validateConfig() error { - if d.inst.Type() != instancetype.Container { + if d.inst.Type() != instancetype.Container && d.inst.Type() != instancetype.VM { return ErrUnsupportedDevType } @@ -61,6 +61,9 @@ func (d *nicMACVLAN) Start() (*deviceConfig.RunConfig, error) { networkCreateSharedDeviceLock.Lock() defer networkCreateSharedDeviceLock.Unlock() + revert := revert.New() + defer revert.Fail() + saveData := make(map[string]string) // Decide which parent we should use based on VLAN setting. @@ -78,20 +81,32 @@ func (d *nicMACVLAN) Start() (*deviceConfig.RunConfig, error) { // Record whether we created the parent device or not so it can be removed on stop. saveData["last_state.created"] = fmt.Sprintf("%t", statusDev != "existing") - // Create MACVLAN interface. - _, err = shared.RunCommand("ip", "link", "add", "dev", saveData["host_name"], "link", parentName, "type", "macvlan", "mode", "bridge") - if err != nil { - return nil, err + if shared.IsTrue(saveData["last_state.created"]) { + revert.Add(func() { + NetworkRemoveInterfaceIfNeeded(d.state, parentName, d.inst, d.config["parent"], d.config["vlan"]) + }) + } + + if d.inst.Type() == instancetype.Container { + // Create MACVLAN interface. + _, err = shared.RunCommand("ip", "link", "add", "dev", saveData["host_name"], "link", parentName, "type", "macvlan", "mode", "bridge") + if err != nil { + return nil, err + } + } else if d.inst.Type() == instancetype.VM { + // Create MACVTAP interface. + _, err = shared.RunCommand("ip", "link", "add", "dev", saveData["host_name"], "link", parentName, "type", "macvtap", "mode", "bridge") + if err != nil { + return nil, err + } } + revert.Add(func() { NetworkRemoveInterface(saveData["host_name"]) }) + // Set the MAC address. if d.config["hwaddr"] != "" { _, err := shared.RunCommand("ip", "link", "set", "dev", saveData["host_name"], "address", d.config["hwaddr"]) if err != nil { - if statusDev == "created" { - NetworkRemoveInterface(saveData["host_name"]) - } - return nil, fmt.Errorf("Failed to set the MAC address: %s", err) } } @@ -100,14 +115,18 @@ func (d *nicMACVLAN) Start() (*deviceConfig.RunConfig, error) { if d.config["mtu"] != "" { _, err := shared.RunCommand("ip", "link", "set", "dev", saveData["host_name"], "mtu", d.config["mtu"]) if err != nil { - if statusDev == "created" { - NetworkRemoveInterface(saveData["host_name"]) - } - return nil, fmt.Errorf("Failed to set the MTU: %s", err) } } + if d.inst.Type() == instancetype.VM { + // Bring the interface up on host side. + _, err := shared.RunCommand("ip", "link", "set", "dev", saveData["host_name"], "up") + if err != nil { + return nil, fmt.Errorf("Failed to bring up interface %s: %v", saveData["host_name"], err) + } + } + err = d.volatileSet(saveData) if err != nil { return nil, err @@ -122,6 +141,13 @@ func (d *nicMACVLAN) Start() (*deviceConfig.RunConfig, error) { {Key: "link", Value: saveData["host_name"]}, } + if d.inst.Type() == instancetype.VM { + runConf.NetworkInterface = append(runConf.NetworkInterface, + deviceConfig.RunConfigItem{Key: "hwaddr", Value: d.config["hwaddr"]}, + ) + } + + revert.Success() return &runConf, nil } From 9fc15f83510188df9dfc0031c2d72d262d8b4730 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 22 Jan 2020 18:18:45 +0000 Subject: [PATCH 4/4] lxd/instance/drivers/driver/qemu: Adds macvtap support Uses macvtap device created by device layer to get a file handle to it and pass it to qemu to use. Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/instance/drivers/driver_qemu.go | 60 +++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 8 deletions(-) diff --git a/lxd/instance/drivers/driver_qemu.go b/lxd/instance/drivers/driver_qemu.go index 0639b907b2..a86bc54aa6 100644 --- a/lxd/instance/drivers/driver_qemu.go +++ b/lxd/instance/drivers/driver_qemu.go @@ -657,7 +657,10 @@ func (vm *qemu) Start(stateful bool) error { return err } - confFile, err := vm.generateQemuConfigFile(qemuType, qemuConfig, devConfs) + // Define a set of files to open and pass their file descriptors to qemu command. + fdFiles := make([]string, 0) + + confFile, err := vm.generateQemuConfigFile(qemuType, qemuConfig, devConfs, &fdFiles) if err != nil { op.Done(err) return err @@ -726,8 +729,25 @@ func (vm *qemu) Start(stateful bool) error { args = append(args, fields...) } - _, err = shared.RunCommand(qemuBinary, args...) + cmd := exec.Command(qemuBinary, args...) + var stdout bytes.Buffer + var stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + + // Open any extra files and pass their file handles to qemu command. + for _, file := range fdFiles { + f, err := os.OpenFile(file, os.O_RDWR, 0) + if err != nil { + return errors.Wrapf(err, "Error opening exta file %q", file) + } + defer f.Close() // Close file after qemu has started. + cmd.ExtraFiles = append(cmd.ExtraFiles, f) + } + + err = cmd.Run() if err != nil { + err = errors.Wrapf(err, "Failed to run: %s %s: %s", qemuBinary, strings.Join(args, " "), strings.TrimSpace(string(stderr.Bytes()))) op.Done(err) return err } @@ -1180,7 +1200,7 @@ func (vm *qemu) deviceBootPriorities() (map[string]int, error) { // generateQemuConfigFile writes the qemu config file and returns its location. // It writes the config file inside the VM's log path. -func (vm *qemu) generateQemuConfigFile(qemuType string, qemuConf string, devConfs []*deviceConfig.RunConfig) (string, error) { +func (vm *qemu) generateQemuConfigFile(qemuType string, qemuConf string, devConfs []*deviceConfig.RunConfig, fdFiles *[]string) (string, error) { var sb *strings.Builder = &strings.Builder{} // Base config. This is common for all VMs and has no variables in it. @@ -1324,7 +1344,7 @@ backend = "pty" // Add network device. if len(runConf.NetworkInterface) > 0 { - err = vm.addNetDevConfig(sb, nicIndex, bootIndexes, runConf.NetworkInterface) + err = vm.addNetDevConfig(sb, nicIndex, bootIndexes, runConf.NetworkInterface, fdFiles) if err != nil { return "", err } @@ -1548,26 +1568,49 @@ bootindex = "{{.bootIndex}}" } // addNetDevConfig adds the qemu config required for adding a network device. -func (vm *qemu) addNetDevConfig(sb *strings.Builder, nicIndex int, bootIndexes map[string]int, nicConfig []deviceConfig.RunConfigItem) error { - var devName, devTap, devHwaddr string +func (vm *qemu) addNetDevConfig(sb *strings.Builder, nicIndex int, bootIndexes map[string]int, nicConfig []deviceConfig.RunConfigItem, fdFiles *[]string) error { + var devName, nicName, devHwaddr string + var tapFD int for _, nicItem := range nicConfig { if nicItem.Key == "devName" { devName = nicItem.Value } else if nicItem.Key == "link" { - devTap = nicItem.Value + nicName = nicItem.Value } else if nicItem.Key == "hwaddr" { devHwaddr = nicItem.Value } } + // Detect MACVTAP interface types and figure out which tap device is being used. + // This is so we can open a file handle to the tap device and pass it to the qemu process. + if shared.PathExists(fmt.Sprintf("/sys/class/net/%s/macvtap", nicName)) { + content, err := ioutil.ReadFile(fmt.Sprintf("/sys/class/net/%s/ifindex", nicName)) + if err != nil { + return errors.Wrapf(err, "Error getting tap device ifindex") + } + + ifindex, err := strconv.Atoi(strings.TrimSpace(string(content))) + if err != nil { + return errors.Wrapf(err, "Error parsing tap device ifindex") + } + + // Append the tap device file path to the list of files to be opened and passed to qemu. + *fdFiles = append(*fdFiles, fmt.Sprintf("/dev/tap%d", ifindex)) + tapFD = 2 + len(*fdFiles) // Use 2+fdFiles count, as first file descriptor available is 3. + } + // Devices use "lxd_" prefix indicating that this is a user named device. t := template.Must(template.New("").Parse(` # Network card ("{{.devName}}" device) [netdev "lxd_{{.devName}}"] type = "tap" +{{ if ne .tapFD 0 }} +fd = "{{.tapFD}}" +{{ else }} ifname = "{{.ifName}}" script = "no" downscript = "no" +{{ end }} [device "qemu_pcie{{.chassisIndex}}"] driver = "pcie-root-port" @@ -1587,12 +1630,13 @@ bootindex = "{{.bootIndex}}" m := map[string]interface{}{ "devName": devName, - "ifName": devTap, + "ifName": nicName, "chassisIndex": 5 + nicIndex, "portIndex": 14 + nicIndex, "pcieAddr": 4 + nicIndex, "devHwaddr": devHwaddr, "bootIndex": bootIndexes[devName], + "tapFD": tapFD, } return t.Execute(sb, m) }
_______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel