The following pull request was submitted through Github.
It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/6721

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) ===
Adds support for adding disk devices to VMs:

Usage: 

```
lxc config device add v1 sdb disk source=/dev/sdb
lxc config device add v1 sdb disk source=/home/my.iso
```
From 54159ecc9bc523ad087ae7b29dfbcd660d8cdbd9 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 16 Jan 2020 11:52:29 +0000
Subject: [PATCH 1/9] lxd/container: Improves error messages in
 instanceValidDevices

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/container.go | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lxd/container.go b/lxd/container.go
index 91332d2514..ef02d21221 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -123,7 +123,7 @@ func instanceValidDevices(state *state.State, cluster 
*db.Cluster, instanceType
        for name, config := range devices {
                _, err := device.New(inst, state, name, config, nil, nil)
                if err != nil {
-                       return err
+                       return errors.Wrapf(err, "Device validation failed %q", 
name)
                }
 
        }
@@ -132,7 +132,7 @@ func instanceValidDevices(state *state.State, cluster 
*db.Cluster, instanceType
        if expanded {
                _, _, err := shared.GetRootDiskDevice(devices.CloneNative())
                if err != nil {
-                       return errors.Wrap(err, "Detect root disk device")
+                       return errors.Wrap(err, "Failed detecting root disk 
device")
                }
        }
 

From db64e3bdbee07808d734ef8ce02ff85465625ba4 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 16 Jan 2020 11:52:47 +0000
Subject: [PATCH 2/9] lxd/container: instance.ValidDevices usage

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/container.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lxd/container.go b/lxd/container.go
index ef02d21221..b912804b1c 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -776,7 +776,7 @@ func instanceCreateInternal(s *state.State, args 
db.InstanceArgs) (instance.Inst
        }
 
        // Validate container devices with the supplied container name and 
devices.
-       err = instanceValidDevices(s, s.Cluster, args.Type, args.Name, 
args.Devices, false)
+       err = instance.ValidDevices(s, s.Cluster, args.Type, args.Name, 
args.Devices, false)
        if err != nil {
                return nil, errors.Wrap(err, "Invalid devices")
        }

From 6432867e3d09c80a409023d8d81ef78096634ee2 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 16 Jan 2020 11:53:03 +0000
Subject: [PATCH 3/9] lxd/container/lxc: instance.ValidDevices usage

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/container_lxc.go | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index d5ce352882..90b6f49508 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -195,7 +195,7 @@ func containerLXCCreate(s *state.State, args 
db.InstanceArgs) (instance.Instance
                return nil, err
        }
 
-       err = instanceValidDevices(s, s.Cluster, c.Type(), c.Name(), 
c.expandedDevices, true)
+       err = instance.ValidDevices(s, s.Cluster, c.Type(), c.Name(), 
c.expandedDevices, true)
        if err != nil {
                c.Delete()
                logger.Error("Failed creating container", ctxMap)
@@ -3905,7 +3905,7 @@ func (c *containerLXC) Update(args db.InstanceArgs, 
userRequested bool) error {
        }
 
        // Validate the new devices without using expanded devices validation 
(expensive checks disabled).
-       err = instanceValidDevices(c.state, c.state.Cluster, c.Type(), 
c.Name(), args.Devices, false)
+       err = instance.ValidDevices(c.state, c.state.Cluster, c.Type(), 
c.Name(), args.Devices, false)
        if err != nil {
                return errors.Wrap(err, "Invalid devices")
        }
@@ -4096,7 +4096,7 @@ func (c *containerLXC) Update(args db.InstanceArgs, 
userRequested bool) error {
        }
 
        // Do full expanded validation of the devices diff.
-       err = instanceValidDevices(c.state, c.state.Cluster, c.Type(), 
c.Name(), c.expandedDevices, true)
+       err = instance.ValidDevices(c.state, c.state.Cluster, c.Type(), 
c.Name(), c.expandedDevices, true)
        if err != nil {
                return errors.Wrap(err, "Invalid expanded devices")
        }

From a085847c141db8330418f40eb749a5a8d65043db Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 16 Jan 2020 11:53:27 +0000
Subject: [PATCH 4/9] lxd/device/config/devices: Improves error messages

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/device/config/devices.go | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lxd/device/config/devices.go b/lxd/device/config/devices.go
index fc91d73218..f6ec31c1e4 100644
--- a/lxd/device/config/devices.go
+++ b/lxd/device/config/devices.go
@@ -27,7 +27,7 @@ func (device Device) Validate(rules map[string]func(value 
string) error) error {
                checkedFields[k] = struct{}{} //Mark field as checked.
                err := validator(device[k])
                if err != nil {
-                       return fmt.Errorf("Invalid value for device option %s: 
%v", k, err)
+                       return fmt.Errorf("Invalid value for device option %q: 
%v", k, err)
                }
        }
 
@@ -47,7 +47,7 @@ func (device Device) Validate(rules map[string]func(value 
string) error) error {
                        continue
                }
 
-               return fmt.Errorf("Invalid device option: %s", k)
+               return fmt.Errorf("Invalid device option %q", k)
        }
 
        return nil

From a28827139be5d7d2ac9f6513d9f02747a85d3eb1 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 16 Jan 2020 11:53:58 +0000
Subject: [PATCH 5/9] lxd/device/disk: Adds support for VM disk devices

Usage: lxc config device add v1 sdb disk source=/dev/sdb

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/device/disk.go | 37 ++++++++++++++++++++++++++-----------
 1 file changed, 26 insertions(+), 11 deletions(-)

diff --git a/lxd/device/disk.go b/lxd/device/disk.go
index a963ece965..95299b1977 100644
--- a/lxd/device/disk.go
+++ b/lxd/device/disk.go
@@ -15,6 +15,7 @@ import (
        "github.com/lxc/lxd/lxd/cgroup"
        "github.com/lxc/lxd/lxd/db"
        deviceConfig "github.com/lxc/lxd/lxd/device/config"
+       "github.com/lxc/lxd/lxd/instance"
        "github.com/lxc/lxd/lxd/instance/instancetype"
        storagePools "github.com/lxc/lxd/lxd/storage"
        "github.com/lxc/lxd/lxd/util"
@@ -83,9 +84,12 @@ func (d *disk) validateConfig() error {
                "ceph.user_name":    shared.IsAny,
        }
 
-       // VMs can have a special cloud-init config drive attached with no 
path. Don't check instance type here
-       // to allow mixed instance type profiles to use this setting. We will 
check at device start for VM type.
-       if d.config["source"] != diskSourceCloudInit {
+       // VMs don't use the "path" property, but containers need it, so if we 
are validating a profile that can
+       // be used for all instance types, we must allow any value.
+       if d.inst.Name() == instance.ProfileValidationName {
+               rules["path"] = shared.IsAny
+       } else if d.inst.Type() == instancetype.Container || d.config["path"] 
== "/" {
+               // If we are validating a container or the root device is being 
validated, then require the value.
                rules["path"] = shared.IsNotEmpty
        }
 
@@ -129,10 +133,10 @@ func (d *disk) validateConfig() error {
        // this can still be cleanly removed.
        pathCount := 0
        for _, devConfig := range d.inst.LocalDevices() {
-               if devConfig["type"] == "disk" && devConfig["path"] == 
d.config["path"] {
+               if devConfig["type"] == "disk" && d.config["path"] != "" && 
devConfig["path"] == d.config["path"] {
                        pathCount++
                        if pathCount > 1 {
-                               return fmt.Errorf("More than one disk device 
uses the same path: %s", d.config["path"])
+                               return fmt.Errorf("More than one disk device 
uses the same path %q", d.config["path"])
 
                        }
                }
@@ -161,10 +165,10 @@ func (d *disk) validateConfig() error {
                }
 
                // Only check storate volume is available if we are validating 
an instance device
-               // and not a profile device (check for non-empty instance 
name), and we have least
+               // and not a profile device (check for ProfileValidationName 
name), and we have least
                // one expanded device (this is so we only do this expensive 
check after devices
                // have been expanded).
-               if d.inst.Name() != "" && len(d.inst.ExpandedDevices()) > 0 && 
d.config["source"] != "" && d.config["path"] != "/" {
+               if d.inst.Name() != instance.ProfileValidationName && 
len(d.inst.ExpandedDevices()) > 0 && d.config["source"] != "" && 
d.config["path"] != "/" {
                        isAvailable, err := 
d.state.Cluster.StorageVolumeIsAvailable(d.config["pool"], d.config["source"])
                        if err != nil {
                                return fmt.Errorf("Check if volume is 
available: %v", err)
@@ -359,12 +363,11 @@ func (d *disk) startVM() (*deviceConfig.RunConfig, error) 
{
        runConf := deviceConfig.RunConfig{}
 
        if shared.IsRootDiskDevice(d.config) {
+               // The root disk device is special as it is given the first 
device ID and boot order in VM 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"] == diskSourceCloudInit {
+       } else if d.config["source"] == diskSourceCloudInit {
+               // This is a special virtual disk source that can be attached 
to a VM to provide cloud-init config.
                isoPath, err := d.generateVMConfigDrive()
                if err != nil {
                        return nil, err
@@ -377,6 +380,18 @@ func (d *disk) startVM() (*deviceConfig.RunConfig, error) {
                        },
                }
                return &runConf, nil
+       } else if d.config["source"] != "" {
+               // This is a normal disk device or image.
+               if !shared.PathExists(d.config["source"]) {
+                       return nil, fmt.Errorf("Cannot find disk source")
+               }
+               runConf.Mounts = []deviceConfig.MountEntryItem{
+                       {
+                               DevPath:    d.config["source"],
+                               TargetPath: d.name,
+                       },
+               }
+               return &runConf, nil
        }
 
        return nil, fmt.Errorf("Disk type not supported for VMs")

From 65238f9388f0f25e4d25da17b21d33d60ad4d98e Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 16 Jan 2020 11:54:53 +0000
Subject: [PATCH 6/9] lxd/instance/instance/interface: Comment ending
 consistency

Also removes out of date TODO comment.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/instance/instance_interface.go | 26 ++++++++++++--------------
 1 file changed, 12 insertions(+), 14 deletions(-)

diff --git a/lxd/instance/instance_interface.go 
b/lxd/instance/instance_interface.go
index 60c9628653..8c9e284e47 100644
--- a/lxd/instance/instance_interface.go
+++ b/lxd/instance/instance_interface.go
@@ -14,9 +14,9 @@ import (
        "github.com/lxc/lxd/shared/api"
 )
 
-// The Instance interface
+// The Instance interface.
 type Instance interface {
-       // Instance actions
+       // Instance actions.
        Freeze() error
        Shutdown(timeout time.Duration) error
        Start(stateful bool) error
@@ -25,27 +25,25 @@ type Instance interface {
 
        IsPrivileged() bool
 
-       // Snapshots & migration & backups
+       // Snapshots & migration & backups.
        Restore(source Instance, stateful bool) error
        Snapshots() ([]Instance, error)
        Backups() ([]backup.Backup, error)
        UpdateBackupFile() error
 
-       // Config handling
+       // Config handling.
        Rename(newName string) error
-
-       // TODO rename db.InstanceArgs to db.InstanceArgs.
        Update(newConfig db.InstanceArgs, userRequested bool) error
 
        Delete() error
        Export(w io.Writer, properties map[string]string) error
 
-       // Live configuration
+       // Live configuration.
        CGroupGet(key string) (string, error)
        CGroupSet(key string, value string) error
        VolatileSet(changes map[string]string) error
 
-       // File handling
+       // File handling.
        FileExists(path string) error
        FilePull(srcpath string, dstpath string) (int64, int64, os.FileMode, 
string, []string, error)
        FilePush(fileType string, srcpath string, dstpath string, uid int64, 
gid int64, mode int, write string) error
@@ -65,10 +63,10 @@ type Instance interface {
        IsSnapshot() bool
        IsStateful() bool
 
-       // Hooks
+       // Hooks.
        DeviceEventHandler(*deviceConfig.RunConfig) error
 
-       // Properties
+       // Properties.
        ID() int
        Location() string
        Project() string
@@ -88,7 +86,7 @@ type Instance interface {
        ExpiryDate() time.Time
        FillNetworkDevice(name string, m deviceConfig.Device) 
(deviceConfig.Device, error)
 
-       // Paths
+       // Paths.
        Path() string
        RootfsPath() string
        TemplatesPath() string
@@ -98,13 +96,13 @@ type Instance interface {
        LogPath() string
        DevicesPath() string
 
-       // Storage
+       // Storage.
        StoragePool() (string, error)
 
-       // Progress reporting
+       // Progress reporting.
        SetOperation(op *operations.Operation)
 
-       // FIXME: Those should be internal functions
+       // FIXME: Those should be internal functions.
        // Needed for migration for now.
        StorageStart() (bool, error)
        StorageStop() (bool, error)

From 2f765d04ed7cd97e509e1e7117e940307352fe08 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 16 Jan 2020 11:55:33 +0000
Subject: [PATCH 7/9] lxd/instance/qemu/vm/qemu: Fixes driver index loop bug

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/instance/qemu/vm_qemu.go | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/lxd/instance/qemu/vm_qemu.go b/lxd/instance/qemu/vm_qemu.go
index 9311b6189f..e5e7690fbc 100644
--- a/lxd/instance/qemu/vm_qemu.go
+++ b/lxd/instance/qemu/vm_qemu.go
@@ -1177,6 +1177,7 @@ backend = "pty"
        vm.addConfDriveConfig(sb)
 
        nicIndex := 0
+       driveIndex := 0
        for _, runConf := range devConfs {
                // Add root drive device.
                if runConf.RootFS.Path != "" {
@@ -1188,11 +1189,9 @@ backend = "pty"
 
                // 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.
+                               // Increment first so index starts at 1, as 
root drive uses index 0.
                                driveIndex++
-
                                vm.addDriveConfig(sb, driveIndex, drive)
                        }
                }

From 1772ce40f39bc0af48a8577dad00b5c9dee9cd8a Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 16 Jan 2020 11:56:14 +0000
Subject: [PATCH 8/9] lxd/instance/instance/utils: Introduces constant to
 indicate profile validation in instance name

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/instance/instance_utils.go | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/lxd/instance/instance_utils.go b/lxd/instance/instance_utils.go
index fa5709106f..3dc21d5537 100644
--- a/lxd/instance/instance_utils.go
+++ b/lxd/instance/instance_utils.go
@@ -32,6 +32,9 @@ import (
        "github.com/lxc/lxd/shared/version"
 )
 
+// ProfileValidationName is an indicator that the instance is just being used 
for profile validation.
+const ProfileValidationName = ""
+
 // ValidDevices is linked from main.instanceValidDevices to validate device 
config. Currently
 // main.instanceValidDevices uses containerLXC internally and so cannot be 
moved from main package.
 var ValidDevices func(state *state.State, cluster *db.Cluster, instanceType 
instancetype.Type, instanceName string, devices deviceConfig.Devices, expanded 
bool) error

From 5fa9a6b0b21129d18749342298bdc81c6daa650c Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 16 Jan 2020 11:56:40 +0000
Subject: [PATCH 9/9] lxd/profiles: Switches to use
 instance.ProfileValidationName during profile validation

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/profiles.go       | 4 ++--
 lxd/profiles_utils.go | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/lxd/profiles.go b/lxd/profiles.go
index 8f2047454a..df37752ecb 100644
--- a/lxd/profiles.go
+++ b/lxd/profiles.go
@@ -109,9 +109,9 @@ func profilesPost(d *Daemon, r *http.Request) 
response.Response {
                return response.BadRequest(err)
        }
 
-       // Validate instance devices with an empty instanceName to indicate 
profile validation.
+       // Validate instance devices with an ProfileValidationName 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)
+       err = instance.ValidDevices(d.State(), d.cluster, 
instancetype.Container, instance.ProfileValidationName, 
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 d949ab2976..3242167c7f 100644
--- a/lxd/profiles_utils.go
+++ b/lxd/profiles_utils.go
@@ -21,9 +21,9 @@ func doProfileUpdate(d *Daemon, project, name string, id 
int64, profile *api.Pro
                return err
        }
 
-       // Validate instance devices with an empty instanceName to indicate 
profile validation.
+       // Validate instance devices with ProfileValidationName name 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)
+       err = instance.ValidDevices(d.State(), d.cluster, 
instancetype.Container, instance.ProfileValidationName, 
deviceConfig.NewDevices(req.Devices), false)
        if err != nil {
                return err
        }
_______________________________________________
lxc-devel mailing list
lxc-devel@lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to