[lxc-devel] [lxd/master] VM cloud-init config drive
tomponline on Github
lxc-bot at linuxcontainers.org
Thu Nov 14 16:04:41 UTC 2019
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 614 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20191114/a0da3ff4/attachment.bin>
-------------- next part --------------
From af3381639e6d2fab782797ca97b7e8a846515386 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Thu, 14 Nov 2019 13:36:44 +0000
Subject: [PATCH 1/9] lxd/device/disk: Adds support for generating VM config
drive
- Splits deviceStart function into startContainer and startVM
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/device/disk.go | 41 ++++++++++++++++++++++++++++++++---------
1 file changed, 32 insertions(+), 9 deletions(-)
diff --git a/lxd/device/disk.go b/lxd/device/disk.go
index a06fc8a81b..c68f6010cc 100644
--- a/lxd/device/disk.go
+++ b/lxd/device/disk.go
@@ -154,7 +154,7 @@ func (d *disk) validateConfig() error {
return fmt.Errorf("Check if volume is available: %v", err)
}
if !isAvailable {
- return fmt.Errorf("Storage volume %q is already attached to a container on a different node", d.config["source"])
+ return fmt.Errorf("Storage volume %q is already attached to an instance on a different node", d.config["source"])
}
}
}
@@ -184,23 +184,23 @@ func (d *disk) CanHotPlug() (bool, []string) {
return true, []string{"limits.max", "limits.read", "limits.write", "size"}
}
-// Start is run when the device is added to the container.
+// Start is run when the device is added to the instance.
func (d *disk) Start() (*RunConfig, error) {
err := d.validateEnvironment()
if err != nil {
return nil, err
}
- runConf := RunConfig{}
-
if d.instance.Type() == instancetype.VM {
- if shared.IsRootDiskDevice(d.config) {
- return &runConf, nil
- }
-
- return nil, fmt.Errorf("Non-root disks not supported for VMs")
+ return d.startVM()
}
+ return d.startContainer()
+}
+
+// startVM starts the disk device for a container instance.
+func (d *disk) startContainer() (*RunConfig, error) {
+ runConf := RunConfig{}
isReadOnly := shared.IsTrue(d.config["readonly"])
// Apply cgroups only after all the mounts have been processed.
@@ -334,6 +334,29 @@ func (d *disk) Start() (*RunConfig, error) {
return &runConf, nil
}
+// startVM starts the disk device for a virtual machine instance.
+func (d *disk) startVM() (*RunConfig, error) {
+ runConf := RunConfig{}
+
+ if shared.IsRootDiskDevice(d.config) {
+ runConf.RootFS.Path = d.config["path"]
+ return &runConf, nil
+ }
+
+ // This is a virtual disk source that can be attached to a VM to provide cloud-init config.
+ if d.config["source"] == "cloud-init:config" {
+ runConf.Mounts = []MountEntryItem{
+ {
+ DevPath: "foo", // Path to generated iso file.
+ TargetPath: d.name,
+ },
+ }
+ return &runConf, nil
+ }
+
+ return nil, fmt.Errorf("Disk type not supported for VMs")
+}
+
// postStart is run after the instance is started.
func (d *disk) postStart() error {
devPath := d.getDevicePath(d.name, d.config)
From 9037a96abcf871a7e215d0169dedbafebacea180 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Thu, 14 Nov 2019 13:37:23 +0000
Subject: [PATCH 2/9] lxd/device/nic/bridged: Adds hwaddr to runConf when
instance type is VM
This allows it to be used inside the VM config to set the tap device to the correct MAC address.
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/device/nic_bridged.go | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/lxd/device/nic_bridged.go b/lxd/device/nic_bridged.go
index 8fd6aa589b..8b18cdb308 100644
--- a/lxd/device/nic_bridged.go
+++ b/lxd/device/nic_bridged.go
@@ -177,6 +177,12 @@ func (d *nicBridged) Start() (*RunConfig, error) {
{Key: "link", Value: peerName},
}
+ if d.instance.Type() == instancetype.VM {
+ runConf.NetworkInterface = append(runConf.NetworkInterface,
+ RunConfigItem{Key: "hwaddr", Value: d.config["hwaddr"]},
+ )
+ }
+
return &runConf, nil
}
From 7742544dfadba7cfc2daf86312d87c7638b2ba45 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Thu, 14 Nov 2019 10:56:04 +0000
Subject: [PATCH 3/9] lxd/vm/qemu: Modifies qemu config generation to support
dynamic devices
- Network
- Supplemental drives
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/vm_qemu.go | 109 +++++++++++++++++++++++++++++++++++++------------
1 file changed, 84 insertions(+), 25 deletions(-)
diff --git a/lxd/vm_qemu.go b/lxd/vm_qemu.go
index 8fa3b683b3..a13b4bdc4f 100644
--- a/lxd/vm_qemu.go
+++ b/lxd/vm_qemu.go
@@ -440,7 +440,7 @@ func (vm *vmQemu) Start(stateful bool) error {
}
}
- tapDev := map[string]string{}
+ devConfs := make([]*device.RunConfig, 0, len(vm.expandedDevices))
// Setup devices in sorted order, this ensures that device mounts are added in path order.
for _, dev := range vm.expandedDevices.Sorted() {
@@ -454,18 +454,10 @@ func (vm *vmQemu) Start(stateful bool) error {
continue
}
- if len(runConf.NetworkInterface) > 0 {
- for _, nicItem := range runConf.NetworkInterface {
- if nicItem.Key == "link" {
- tapDev["tap"] = nicItem.Value
- tapDev["hwaddr"] = vm.localConfig[fmt.Sprintf("volatile.%s.hwaddr", dev.Name)]
- }
- }
-
- }
+ devConfs = append(devConfs, runConf)
}
- confFile, err := vm.generateQemuConfigFile(tapDev)
+ confFile, err := vm.generateQemuConfigFile(devConfs)
if err != nil {
return err
}
@@ -768,7 +760,7 @@ WantedBy=multi-user.target
}
// generateQemuConfigFile writes the qemu config file and returns its location.
-func (vm *vmQemu) generateQemuConfigFile(tapDev map[string]string) (string, error) {
+func (vm *vmQemu) generateQemuConfigFile(devConfs []*device.RunConfig) (string, error) {
var sb *strings.Builder = &strings.Builder{}
// Base config. This is common for all VMs and has no variables in it.
@@ -858,11 +850,6 @@ backend = "pty"
return "", err
}
- err = vm.addRootDriveConfig(sb)
- if err != nil {
- return "", err
- }
-
err = vm.addCPUConfig(sb)
if err != nil {
return "", err
@@ -872,13 +859,39 @@ backend = "pty"
vm.addVsockConfig(sb)
vm.addMonitorConfig(sb)
vm.addConfDriveConfig(sb)
- vm.addNetConfig(sb, tapDev)
+
+ for _, runConf := range devConfs {
+ // Add root drive device.
+ if runConf.RootFS.Path != "" {
+ err = vm.addRootDriveConfig(sb)
+ if err != nil {
+ return "", err
+ }
+ }
+
+ // Add drive devices.
+ if len(runConf.Mounts) > 0 {
+ driveIndex := 0
+ for _, drive := range runConf.Mounts {
+ // Increment so index starts at 1, as root drive uses index 0.
+ driveIndex++
+
+ vm.addDriveConfig(sb, driveIndex, drive)
+ }
+ }
+
+ // Add network device.
+ if len(runConf.NetworkInterface) > 0 {
+ vm.addNetDevConfig(sb, runConf.NetworkInterface)
+ }
+ }
// Write the config file to disk.
configPath := filepath.Join(vm.LogPath(), "qemu.conf")
return configPath, ioutil.WriteFile(configPath, []byte(sb.String()), 0640)
}
+// addMemoryConfig adds the qemu config required for setting the size of the VM's memory.
func (vm *vmQemu) addMemoryConfig(sb *strings.Builder) error {
// Configure memory limit.
memSize := vm.expandedConfig["limits.memory"]
@@ -902,6 +915,7 @@ size = "%dK"
return nil
}
+// addVsockConfig adds the qemu config required for setting up the host->VM vsock socket.
func (vm *vmQemu) addVsockConfig(sb *strings.Builder) {
vsockID := vm.vsockID()
@@ -924,6 +938,7 @@ addr = "0x0"
return
}
+// addCPUConfig adds the qemu config required for setting the number of virtualised CPUs.
func (vm *vmQemu) addCPUConfig(sb *strings.Builder) error {
// Configure CPU limit. TODO add control of sockets, cores and threads.
cpus := vm.expandedConfig["limits.cpu"]
@@ -948,6 +963,7 @@ cpus = "%d"
return nil
}
+// addMonitorConfig adds the qemu config required for setting up the host side VM monitor device.
func (vm *vmQemu) addMonitorConfig(sb *strings.Builder) {
monitorPath := vm.getMonitorPath()
@@ -967,6 +983,7 @@ mode = "control"
return
}
+// addFirmwareConfig adds the qemu config required for adding a secure boot compatible EFI firmware.
func (vm *vmQemu) addFirmwareConfig(sb *strings.Builder) {
nvramPath := vm.getNvramPath()
@@ -989,6 +1006,7 @@ unit = "1"
return
}
+// addRootDriveConfig adds the qemu config required for adding the root drive.
func (vm *vmQemu) addRootDriveConfig(sb *strings.Builder) error {
pool, err := storagePools.GetPoolByInstance(vm.state, vm)
if err != nil {
@@ -1022,28 +1040,66 @@ bootindex = "1"
return nil
}
+// addConfDriveConfig adds the qemu config required for adding the config drive.
func (vm *vmQemu) addConfDriveConfig(sb *strings.Builder) {
sb.WriteString(fmt.Sprintf(`
# Config drive
-[fsdev "qemu_config"]
+[fsdev "lxd_config"]
fsdriver = "local"
security_model = "none"
readonly = "on"
path = "%s"
-[device "dev-qemu_config"]
+[device "dev-lxd_config"]
driver = "virtio-9p-pci"
-fsdev = "qemu_config"
+fsdev = "lxd_config"
mount_tag = "config"
`, filepath.Join(vm.Path(), "config")))
return
}
-func (vm *vmQemu) addNetConfig(sb *strings.Builder, tapDev map[string]string) {
+// addDriveConfig adds the qemu config required for adding a supplementary drive.
+func (vm *vmQemu) addDriveConfig(sb *strings.Builder, driveIndex int, driveConf device.MountEntryItem) {
+ driveName := fmt.Sprintf(driveConf.TargetPath)
+
+ sb.WriteString(fmt.Sprintf(`
+# %s drive
+[drive "lxd_disk_%s"]
+file = "%s"
+format = "raw"
+if = "none"
+cache = "none"
+aio = "native"
+
+[device "dev-lxd_disk_%s"]
+driver = "scsi-hd"
+bus = "qemu_scsi.0"
+channel = "0"
+scsi-id = "%d"
+lun = "1"
+drive = "lxd_disk_%s"
+`, driveName, driveName, driveConf.DevPath, driveName, driveIndex, driveName))
+
+ return
+}
+
+// addNetDevConfig adds the qemu config required for adding a network device.
+func (vm *vmQemu) addNetDevConfig(sb *strings.Builder, nicConfig []device.RunConfigItem) {
+ var devName, devTap, devHwaddr string
+ for _, nicItem := range nicConfig {
+ if nicItem.Key == "name" {
+ devName = nicItem.Value
+ } else if nicItem.Key == "link" {
+ devTap = nicItem.Value
+ } else if nicItem.Key == "hwaddr" {
+ devHwaddr = nicItem.Value
+ }
+ }
+
sb.WriteString(fmt.Sprintf(`
-# Network card ("eth0" device)
-[netdev "lxd_eth0"]
+# Network card ("%s" device)
+[netdev "lxd_%s"]
type = "tap"
ifname = "%s"
script = "no"
@@ -1063,15 +1119,17 @@ mac = "%s"
bus = "qemu_pcie5"
addr = "0x0"
bootindex = "2""
-`, tapDev["tap"], tapDev["hwaddr"]))
+`, devName, devName, devTap, devHwaddr))
return
}
+// pidFilePath returns the path where the qemu process should write its PID.
func (vm *vmQemu) pidFilePath() string {
return filepath.Join(vm.LogPath(), "qemu.pid")
}
+// pid gets the PID of the running qemu process.
func (vm *vmQemu) pid() (int, error) {
pidStr, err := ioutil.ReadFile(vm.pidFilePath())
if os.IsNotExist(err) {
@@ -1090,6 +1148,7 @@ func (vm *vmQemu) pid() (int, error) {
return pid, nil
}
+// Stop stops the VM.
func (vm *vmQemu) Stop(stateful bool) error {
if stateful {
return fmt.Errorf("Stateful stop isn't supported for VMs at this time")
From 48633beed2bdc03dfb2b55bdae7230f53d5d7c01 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Thu, 14 Nov 2019 15:17:45 +0000
Subject: [PATCH 4/9] lxd/container: Renames containerValidDevices to
instanceValidDevices
And updates to take an instancetype.Type argument so that validation can be instance type specific.
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/container.go | 41 +++++++++++++++++++++++++++++++----------
1 file changed, 31 insertions(+), 10 deletions(-)
diff --git a/lxd/container.go b/lxd/container.go
index 81937a8843..9652a4ba11 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -195,28 +195,49 @@ func containerValidConfig(sysOS *sys.OS, config map[string]string, profile bool,
return nil
}
-// containerValidDevices validate container device configs.
-func containerValidDevices(state *state.State, cluster *db.Cluster, instanceName string, devices deviceConfig.Devices, expanded bool) error {
+// instanceValidDevices validate instance device configs.
+func instanceValidDevices(state *state.State, cluster *db.Cluster, instanceType instancetype.Type, instanceName string, devices deviceConfig.Devices, expanded bool) error {
// Empty device list
if devices == nil {
return nil
}
- // Create a temporary containerLXC struct to use as an Instance in device validation.
+ // Create a temporary Instance for use in device validation.
// Populate it's name, localDevices and expandedDevices properties based on the mode of
// validation occurring. In non-expanded validation expensive checks should be avoided.
- instance := &containerLXC{
- name: instanceName,
- localDevices: devices.Clone(), // Prevent devices from modifying their config.
- }
+ var inst Instance
- if expanded {
- instance.expandedDevices = instance.localDevices // Avoid another clone.
+ if instanceType == instancetype.Container {
+ c := &containerLXC{
+ dbType: instancetype.Container,
+ name: instanceName,
+ localDevices: devices.Clone(), // Prevent devices from modifying their config.
+ }
+
+ if expanded {
+ c.expandedDevices = c.localDevices // Avoid another clone.
+ }
+
+ inst = c
+ } else if instanceType == instancetype.VM {
+ vm := &vmQemu{
+ dbType: instancetype.VM,
+ name: instanceName,
+ localDevices: devices.Clone(), // Prevent devices from modifying their config.
+ }
+
+ if expanded {
+ vm.expandedDevices = vm.localDevices // Avoid another clone.
+ }
+
+ inst = vm
+ } else {
+ return fmt.Errorf("Invalid instance type")
}
// Check each device individually using the device package.
for name, config := range devices {
- _, err := device.New(instance, state, name, config, nil, nil)
+ _, err := device.New(inst, state, name, config, nil, nil)
if err != nil {
return err
}
From d79911116814e173187fe8699aedec0b80a2c13e Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Thu, 14 Nov 2019 15:18:38 +0000
Subject: [PATCH 5/9] lxd/device/device/instance: Adds Path() to Instance
interface
So can be used by disk device.
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/device/device_instance.go | 1 +
1 file changed, 1 insertion(+)
diff --git a/lxd/device/device_instance.go b/lxd/device/device_instance.go
index 296bcb2bdc..5063939d54 100644
--- a/lxd/device/device_instance.go
+++ b/lxd/device/device_instance.go
@@ -12,6 +12,7 @@ type Instance interface {
Name() string
Type() instancetype.Type
Project() string
+ Path() string
DevicesPath() string
RootfsPath() string
LogPath() string
From 072bee6c61130619f4047c3b3fb399cd3d60c872 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Thu, 14 Nov 2019 15:23:05 +0000
Subject: [PATCH 6/9] lxd/device/disk: Adds support for generating VM
cloud-init config drive
Adds support for a special VM disk device with a source of "cloud-init:config".
lxc config device add v1 config disk source=cloud-init:config
This generates an ISO containing the cloud-init config with a label of "cidata" so that cloud-init inside the VM detects it.
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/device/disk.go | 82 +++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 78 insertions(+), 4 deletions(-)
diff --git a/lxd/device/disk.go b/lxd/device/disk.go
index c68f6010cc..107d8405be 100644
--- a/lxd/device/disk.go
+++ b/lxd/device/disk.go
@@ -22,6 +22,9 @@ import (
"github.com/lxc/lxd/shared/units"
)
+// Special disk "source" value used for generating a VM cloud-init config ISO.
+const diskSourceCloudInit = "cloud-init:config"
+
type diskBlockLimit struct {
readBps int64
readIops int64
@@ -62,7 +65,6 @@ func (d *disk) validateConfig() error {
}
rules := map[string]func(string) error{
- "path": shared.IsNotEmpty,
"required": shared.IsBool,
"optional": shared.IsBool, // "optional" is deprecated, replaced by "required".
"readonly": shared.IsBool,
@@ -78,6 +80,11 @@ func (d *disk) validateConfig() error {
"raw.mount.options": shared.IsAny,
}
+ // VMs can have a special cloud-init config drive attached with no path.
+ if d.instance.Type() != instancetype.VM || d.config["source"] != diskSourceCloudInit {
+ rules["path"] = shared.IsNotEmpty
+ }
+
err := d.config.Validate(rules)
if err != nil {
return err
@@ -126,7 +133,7 @@ func (d *disk) validateConfig() error {
// When we want to attach a storage volume created via the storage api the "source" only
// contains the name of the storage volume, not the path where it is mounted. So only check
// for the existence of "source" when "pool" is empty.
- if d.config["pool"] == "" && d.config["source"] != "" && d.isRequired(d.config) && !shared.PathExists(shared.HostPath(d.config["source"])) {
+ if d.config["pool"] == "" && d.config["source"] != "" && d.config["source"] != diskSourceCloudInit && d.isRequired(d.config) && !shared.PathExists(shared.HostPath(d.config["source"])) {
return fmt.Errorf("Missing source '%s' for disk '%s'", d.config["source"], d.name)
}
@@ -344,10 +351,15 @@ func (d *disk) startVM() (*RunConfig, error) {
}
// This is a virtual disk source that can be attached to a VM to provide cloud-init config.
- if d.config["source"] == "cloud-init:config" {
+ if d.config["source"] == diskSourceCloudInit {
+ isoPath, err := d.generateVMConfigDrive()
+ if err != nil {
+ return nil, err
+ }
+
runConf.Mounts = []MountEntryItem{
{
- DevPath: "foo", // Path to generated iso file.
+ DevPath: isoPath,
TargetPath: d.name,
},
}
@@ -1035,3 +1047,65 @@ func (d *disk) getParentBlocks(path string) ([]string, error) {
return devices, nil
}
+
+// generateVMConfigDrive generates an ISO containing the cloud init config for a VM.
+// Returns the path to the ISO.
+func (d *disk) generateVMConfigDrive() (string, error) {
+ configDrivePath := filepath.Join(d.instance.Path(), "configdrv")
+
+ // Create config drive dir.
+ err := os.MkdirAll(configDrivePath, 0100)
+ if err != nil {
+ return "", err
+ }
+
+ // Add config drive mount instructions to cloud init.
+ vendorData := `#cloud-config
+runcmd:
+ - "mkdir /media/lxd_config"
+ - "mount -o ro -t iso9660 /dev/disk/by-label/cidata /media/lxd_config"
+ - "cp /media/lxd_config/media-lxd_config.mount /etc/systemd/system/"
+ - "systemctl enable media-lxd_config.mount"`
+
+ err = ioutil.WriteFile(filepath.Join(configDrivePath, "vendor-data"), []byte(vendorData), 0400)
+ if err != nil {
+ return "", err
+ }
+
+ instanceConfig := d.instance.ExpandedConfig()
+ userData := instanceConfig["user.user-data"]
+
+ // Use an empty user-data file if no custom user-data supplied.
+ if userData == "" {
+ userData = "#cloud-config"
+ }
+
+ err = ioutil.WriteFile(filepath.Join(configDrivePath, "/user-data"), []byte(userData), 0400)
+ if err != nil {
+ return "", err
+ }
+
+ metaData := fmt.Sprintf(`instance-id: %s
+local-hostname: %s
+`, d.instance.Name(), d.instance.Name())
+
+ err = ioutil.WriteFile(filepath.Join(configDrivePath, "meta-data"), []byte(metaData), 0400)
+ if err != nil {
+ return "", err
+ }
+
+ // Finally convert the config drive dir into an ISO file. The cidata label is important
+ // as this is what cloud-init uses to detect, mount the drive and run the cloud-init
+ // templates on first boot. The vendor-data template then modifies the system so that the
+ // config drive is mounted and the agent is started on subsequent boots.
+ isoPath := filepath.Join(d.instance.Path(), "config.iso")
+ _, err = shared.RunCommand("mkisofs", "-R", "-V", "cidata", "-o", isoPath, configDrivePath)
+ if err != nil {
+ return "", err
+ }
+
+ // Remove the config drive folder.
+ os.RemoveAll(configDrivePath)
+
+ return isoPath, nil
+}
From 96ce20bab87b21bd7672d305e080eb736e0976ae Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Thu, 14 Nov 2019 15:25:10 +0000
Subject: [PATCH 7/9] lxd: Updates instanceValidDevices usage
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/container.go | 2 +-
lxd/container_lxc.go | 6 +++---
lxd/profiles.go | 6 ++++--
lxd/profiles_utils.go | 6 ++++--
lxd/vm_qemu.go | 6 +++---
5 files changed, 15 insertions(+), 11 deletions(-)
diff --git a/lxd/container.go b/lxd/container.go
index 9652a4ba11..fde7831bac 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -876,7 +876,7 @@ func instanceCreateInternal(s *state.State, args db.InstanceArgs) (Instance, err
}
// Validate container devices with the supplied container name and devices.
- err = containerValidDevices(s, s.Cluster, args.Name, args.Devices, false)
+ err = instanceValidDevices(s, s.Cluster, args.Type, args.Name, args.Devices, false)
if err != nil {
return nil, errors.Wrap(err, "Invalid devices")
}
diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index 32bc30e175..7edf6d3beb 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -295,7 +295,7 @@ func containerLXCCreate(s *state.State, args db.InstanceArgs) (container, error)
return nil, err
}
- err = containerValidDevices(s, s.Cluster, c.Name(), c.expandedDevices, true)
+ err = instanceValidDevices(s, s.Cluster, c.Type(), c.Name(), c.expandedDevices, true)
if err != nil {
c.Delete()
logger.Error("Failed creating container", ctxMap)
@@ -4130,7 +4130,7 @@ func (c *containerLXC) Update(args db.InstanceArgs, userRequested bool) error {
}
// Validate the new devices without using expanded devices validation (expensive checks disabled).
- err = containerValidDevices(c.state, c.state.Cluster, c.Name(), args.Devices, false)
+ err = instanceValidDevices(c.state, c.state.Cluster, c.Type(), c.Name(), args.Devices, false)
if err != nil {
return errors.Wrap(err, "Invalid devices")
}
@@ -4321,7 +4321,7 @@ func (c *containerLXC) Update(args db.InstanceArgs, userRequested bool) error {
}
// Do full expanded validation of the devices diff.
- err = containerValidDevices(c.state, c.state.Cluster, c.Name(), c.expandedDevices, true)
+ err = instanceValidDevices(c.state, c.state.Cluster, c.Type(), c.Name(), c.expandedDevices, true)
if err != nil {
return errors.Wrap(err, "Invalid expanded devices")
}
diff --git a/lxd/profiles.go b/lxd/profiles.go
index 424a9e0b47..91b488dc6d 100644
--- a/lxd/profiles.go
+++ b/lxd/profiles.go
@@ -15,6 +15,7 @@ import (
"github.com/lxc/lxd/lxd/cluster"
"github.com/lxc/lxd/lxd/db"
deviceConfig "github.com/lxc/lxd/lxd/device/config"
+ "github.com/lxc/lxd/lxd/instance/instancetype"
"github.com/lxc/lxd/lxd/response"
"github.com/lxc/lxd/lxd/util"
"github.com/lxc/lxd/shared"
@@ -107,8 +108,9 @@ func profilesPost(d *Daemon, r *http.Request) response.Response {
return response.BadRequest(err)
}
- // Validate container devices with an empty instanceName to indicate profile validation.
- err = containerValidDevices(d.State(), d.cluster, "", deviceConfig.NewDevices(req.Devices), false)
+ // Validate instance devices with an empty instanceName to indicate profile validation.
+ // At this point we don't know the instance type, so just use Container type for validation.
+ err = instanceValidDevices(d.State(), d.cluster, instancetype.Container, "", deviceConfig.NewDevices(req.Devices), false)
if err != nil {
return response.BadRequest(err)
}
diff --git a/lxd/profiles_utils.go b/lxd/profiles_utils.go
index c483af6f5b..3f8d8d5a54 100644
--- a/lxd/profiles_utils.go
+++ b/lxd/profiles_utils.go
@@ -7,6 +7,7 @@ import (
"github.com/lxc/lxd/lxd/db"
"github.com/lxc/lxd/lxd/db/query"
deviceConfig "github.com/lxc/lxd/lxd/device/config"
+ "github.com/lxc/lxd/lxd/instance/instancetype"
"github.com/lxc/lxd/shared"
"github.com/lxc/lxd/shared/api"
"github.com/pkg/errors"
@@ -19,8 +20,9 @@ func doProfileUpdate(d *Daemon, project, name string, id int64, profile *api.Pro
return err
}
- // Validate container devices with an empty instanceName to indicate profile validation.
- err = containerValidDevices(d.State(), d.cluster, "", deviceConfig.NewDevices(req.Devices), false)
+ // Validate instance devices with an empty instanceName to indicate profile validation.
+ // At this point we don't know the instance type, so just use Container type for validation.
+ err = instanceValidDevices(d.State(), d.cluster, instancetype.Container, "", deviceConfig.NewDevices(req.Devices), false)
if err != nil {
return err
}
diff --git a/lxd/vm_qemu.go b/lxd/vm_qemu.go
index a13b4bdc4f..263f660544 100644
--- a/lxd/vm_qemu.go
+++ b/lxd/vm_qemu.go
@@ -172,7 +172,7 @@ func vmQemuCreate(s *state.State, args db.InstanceArgs) (Instance, error) {
return nil, err
}
- err = containerValidDevices(s, s.Cluster, vm.Name(), vm.expandedDevices, true)
+ err = instanceValidDevices(s, s.Cluster, vm.Type(), vm.Name(), vm.expandedDevices, true)
if err != nil {
logger.Error("Failed creating instance", ctxMap)
return nil, errors.Wrap(err, "Invalid devices")
@@ -1262,7 +1262,7 @@ func (vm *vmQemu) Update(args db.InstanceArgs, userRequested bool) error {
}
// Validate the new devices without using expanded devices validation (expensive checks disabled).
- err = containerValidDevices(vm.state, vm.state.Cluster, vm.Name(), args.Devices, false)
+ err = instanceValidDevices(vm.state, vm.state.Cluster, vm.Type(), vm.Name(), args.Devices, false)
if err != nil {
return errors.Wrap(err, "Invalid devices")
}
@@ -1446,7 +1446,7 @@ func (vm *vmQemu) Update(args db.InstanceArgs, userRequested bool) error {
}
// Do full expanded validation of the devices diff.
- err = containerValidDevices(vm.state, vm.state.Cluster, vm.Name(), vm.expandedDevices, true)
+ err = instanceValidDevices(vm.state, vm.state.Cluster, vm.Type(), vm.Name(), vm.expandedDevices, true)
if err != nil {
return errors.Wrap(err, "Invalid expanded devices")
}
From 96f32d5c7e7d58ca961b7a36622829c7e17a46ef Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Thu, 14 Nov 2019 15:51:54 +0000
Subject: [PATCH 8/9] lxd: Fixes bug in fillNetworkDevice volatile hwaddr
generation
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/container_lxc.go | 3 ++-
lxd/vm_qemu.go | 4 +++-
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index 7edf6d3beb..a364ac8a0b 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -6485,6 +6485,7 @@ func (c *containerLXC) removeUnixDevices() error {
// fillNetworkDevice takes a nic or infiniband device type and enriches it with automatically
// generated name and hwaddr properties if these are missing from the device.
func (c *containerLXC) fillNetworkDevice(name string, m deviceConfig.Device) (deviceConfig.Device, error) {
+ var err error
newDevice := m.Clone()
// Function to try and guess an available name
@@ -6578,7 +6579,7 @@ func (c *containerLXC) fillNetworkDevice(name string, m deviceConfig.Device) (de
volatileHwaddr := c.localConfig[configKey]
if volatileHwaddr == "" {
// Generate a new MAC address
- volatileHwaddr, err := deviceNextInterfaceHWAddr()
+ volatileHwaddr, err = deviceNextInterfaceHWAddr()
if err != nil {
return nil, err
}
diff --git a/lxd/vm_qemu.go b/lxd/vm_qemu.go
index 263f660544..e0332ca7ff 100644
--- a/lxd/vm_qemu.go
+++ b/lxd/vm_qemu.go
@@ -2478,6 +2478,8 @@ func (vm *vmQemu) DaemonState() *state.State {
// fillNetworkDevice takes a nic or infiniband device type and enriches it with automatically
// generated name and hwaddr properties if these are missing from the device.
func (vm *vmQemu) fillNetworkDevice(name string, m deviceConfig.Device) (deviceConfig.Device, error) {
+ var err error
+
newDevice := m.Clone()
updateKey := func(key string, value string) error {
tx, err := vm.state.Cluster.Begin()
@@ -2505,7 +2507,7 @@ func (vm *vmQemu) fillNetworkDevice(name string, m deviceConfig.Device) (deviceC
volatileHwaddr := vm.localConfig[configKey]
if volatileHwaddr == "" {
// Generate a new MAC address
- volatileHwaddr, err := deviceNextInterfaceHWAddr()
+ volatileHwaddr, err = deviceNextInterfaceHWAddr()
if err != nil {
return nil, err
}
From a09b5a4efb9581f772cf9a23ee20b1718bdc70d4 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Thu, 14 Nov 2019 16:02:55 +0000
Subject: [PATCH 9/9] lxd/vm/qemu: Fix root disk path in device
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/vm_qemu.go | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lxd/vm_qemu.go b/lxd/vm_qemu.go
index e0332ca7ff..14707f7d24 100644
--- a/lxd/vm_qemu.go
+++ b/lxd/vm_qemu.go
@@ -1021,8 +1021,8 @@ func (vm *vmQemu) addRootDriveConfig(sb *strings.Builder) error {
sb.WriteString(fmt.Sprintf(`
# Root drive ("root" device)
[drive "lxd_root"]
-file = "raw"
-format = "%s"
+file = "%s"
+format = "raw"
if = "none"
cache = "none"
aio = "native"
More information about the lxc-devel
mailing list