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

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) ===
This PR removes the somewhat hacky `ProfileValidationName` constant that used an empty instanceName as a way to indicate to device validation that the 'instance' being validated was in fact a profile type and to allow validation to be somewhat more relaxed to accommodate a device being defined in the same profile for different instance types.

Instead we now remove `instanceName` entirely from device validation and instead use the `instancetype.Any` type to indicate profile validation (i.e the instance type being validated could be "any").

In order to support this, a `device.Validate()` function has been added, which accepts a new interface type `instance.ConfigReader` which allows only access to an instance's config, which allow us to instantiate an instance of type "Any" that contains the profile device. config for validation. This is then passed as an argument to each device's `validateConfig()` function.

Additionally a tentative first step at a `common` instance type implementation has been added, and embedded into the `qemu` driver. At this time this common instance only deals with config access and expanding config. However it is expected that more common instance functionality will be moved across once the `containerLXC` type has been moved into `instance/drivers` package.

From 8be13df905cbaed9f1eb57c88014e8f65313261a Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 30 Jan 2020 13:25:21 +0000
Subject: [PATCH 01/12] lxd/device/device/common: Splits device common into own
 file

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/device/device_common.go | 58 +++++++++++++++++++++++++++++++++++++
 1 file changed, 58 insertions(+)
 create mode 100644 lxd/device/device_common.go

diff --git a/lxd/device/device_common.go b/lxd/device/device_common.go
new file mode 100644
index 0000000000..2f6551f5d3
--- /dev/null
+++ b/lxd/device/device_common.go
@@ -0,0 +1,58 @@
+package device
+
+import (
+       "fmt"
+
+       deviceConfig "github.com/lxc/lxd/lxd/device/config"
+       "github.com/lxc/lxd/lxd/instance"
+       "github.com/lxc/lxd/lxd/state"
+)
+
+// deviceCommon represents the common struct for all devices.
+type deviceCommon struct {
+       inst        instance.Instance
+       name        string
+       config      deviceConfig.Device
+       state       *state.State
+       volatileGet func() map[string]string
+       volatileSet func(map[string]string) error
+}
+
+// init stores the Instance, daemon state, device name and config into device.
+// It also needs to be provided with volatile get and set functions for the 
device to allow
+// persistent data to be accessed. This is implemented as part of deviceCommon 
so that the majority
+// of devices don't need to implement it and can just embed deviceCommon.
+func (d *deviceCommon) init(inst instance.Instance, state *state.State, name 
string, conf deviceConfig.Device, volatileGet VolatileGetter, volatileSet 
VolatileSetter) {
+       d.inst = inst
+       d.name = name
+       d.config = conf
+       d.state = state
+       d.volatileGet = volatileGet
+       d.volatileSet = volatileSet
+}
+
+// Add returns nil error as majority of devices don't need to do any host-side 
setup.
+func (d *deviceCommon) Add() error {
+       return nil
+}
+
+// Register returns nil error as majority of devices don't need to do any 
event registration.
+func (d *deviceCommon) Register() error {
+       return nil
+}
+
+// CanHotPlug returns true as majority of devices can be started/stopped when 
instance is running.
+// Also returns an empty list of update fields as most devices do not support 
live updates.
+func (d *deviceCommon) CanHotPlug() (bool, []string) {
+       return true, []string{}
+}
+
+// Update returns an error as most devices do not support live updates without 
being restarted.
+func (d *deviceCommon) Update(oldDevices deviceConfig.Devices, isRunning bool) 
error {
+       return fmt.Errorf("Device does not support updates whilst started")
+}
+
+// Remove returns nil error as majority of devices don't need to do any 
host-side cleanup on delete.
+func (d *deviceCommon) Remove() error {
+       return nil
+}

From 00c762a7f36d8431bc8fb9c67cc197594223b044 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 30 Jan 2020 13:25:39 +0000
Subject: [PATCH 02/12] lxd/device/device: Removes original device.go file

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/device/device.go | 163 -------------------------------------------
 1 file changed, 163 deletions(-)
 delete mode 100644 lxd/device/device.go

diff --git a/lxd/device/device.go b/lxd/device/device.go
deleted file mode 100644
index af9e4a07ed..0000000000
--- a/lxd/device/device.go
+++ /dev/null
@@ -1,163 +0,0 @@
-package device
-
-import (
-       "fmt"
-
-       deviceConfig "github.com/lxc/lxd/lxd/device/config"
-       "github.com/lxc/lxd/lxd/instance"
-       "github.com/lxc/lxd/lxd/state"
-)
-
-// devTypes defines supported top-level device type creation functions.
-var devTypes = map[string]func(deviceConfig.Device) device{
-       "nic":          nicLoadByType,
-       "infiniband":   infinibandLoadByType,
-       "proxy":        func(c deviceConfig.Device) device { return &proxy{} },
-       "gpu":          func(c deviceConfig.Device) device { return &gpu{} },
-       "usb":          func(c deviceConfig.Device) device { return &usb{} },
-       "unix-char":    func(c deviceConfig.Device) device { return 
&unixCommon{} },
-       "unix-block":   func(c deviceConfig.Device) device { return 
&unixCommon{} },
-       "unix-hotplug": func(c deviceConfig.Device) device { return 
&unixHotplug{} },
-       "disk":         func(c deviceConfig.Device) device { return &disk{} },
-       "none":         func(c deviceConfig.Device) device { return &none{} },
-}
-
-// VolatileSetter is a function that accepts one or more key/value strings to 
save into the LXD
-// config for this instance. It should add the volatile device name prefix to 
each key when saving.
-type VolatileSetter func(map[string]string) error
-
-// VolatileGetter is a function that retrieves any key/value string that 
exists in the LXD database
-// config for this instance. It should only return keys that match the 
volatile device name prefix,
-// and should remove the prefix before being returned.
-type VolatileGetter func() map[string]string
-
-// Device represents a device that can be added to an instance.
-type Device interface {
-       // CanHotPlug returns true if device can be managed whilst instance is 
running.
-       // It also returns a slice of config fields that can be live updated. 
If only fields in this
-       // list have changed then Update() is called rather than triggering a 
device remove & add.
-       CanHotPlug() (bool, []string)
-
-       // Add performs any host-side setup when a device is added to an 
instance.
-       // It is called irrespective of whether the instance is running or not.
-       Add() error
-
-       // Start peforms any host-side configuration required to start the 
device for the instance.
-       // This can be when a device is plugged into a running instance or the 
instance is starting.
-       // Returns run-time configuration needed for configuring the instance 
with the new device.
-       Start() (*deviceConfig.RunConfig, error)
-
-       // Register provides the ability for a device to subcribe to events 
that LXD can generate.
-       // It is called after a device is started (after Start()) or when LXD 
starts.
-       Register() error
-
-       // Update performs host-side modifications for a device based on the 
difference between the
-       // current config and previous devices config supplied as an argument. 
This called if the
-       // only config fields that have changed are supplied in the list 
returned from CanHotPlug().
-       // The function also accepts a boolean indicating whether the instance 
is running or not.
-       Update(oldDevices deviceConfig.Devices, running bool) error
-
-       // Stop performs any host-side cleanup required when a device is 
removed from an instance,
-       // either due to unplugging it from a running instance or instance is 
being shutdown.
-       // Returns run-time configuration needed for detaching the device from 
the instance.
-       Stop() (*deviceConfig.RunConfig, error)
-
-       // Remove performs any host-side cleanup when an instance is removed 
from an instance.
-       Remove() error
-}
-
-// device represents a sealed interface that implements Device, but also 
contains some internal
-// setup functions for a Device that should only be called by device.New() to 
avoid exposing devices
-// that are not in a known configured state. This is separate from the Device 
interface so that
-// Devices created outside of the device package can be used by LXD, but 
ensures that any devices
-// created by the device package will only be accessible after being 
configured properly by New().
-type device interface {
-       Device
-
-       // init stores the Instance, daemon State and Config into device and 
performs any setup.
-       init(instance.Instance, *state.State, string, deviceConfig.Device, 
VolatileGetter, VolatileSetter)
-
-       // validateConfig checks Config stored by init() is valid for the 
instance type.
-       validateConfig() error
-}
-
-// deviceCommon represents the common struct for all devices.
-type deviceCommon struct {
-       inst        instance.Instance
-       name        string
-       config      deviceConfig.Device
-       state       *state.State
-       volatileGet func() map[string]string
-       volatileSet func(map[string]string) error
-}
-
-// init stores the Instance, daemon state, device name and config into device.
-// It also needs to be provided with volatile get and set functions for the 
device to allow
-// persistent data to be accessed. This is implemented as part of deviceCommon 
so that the majority
-// of devices don't need to implement it and can just embed deviceCommon.
-func (d *deviceCommon) init(inst instance.Instance, state *state.State, name 
string, conf deviceConfig.Device, volatileGet VolatileGetter, volatileSet 
VolatileSetter) {
-       d.inst = inst
-       d.name = name
-       d.config = conf
-       d.state = state
-       d.volatileGet = volatileGet
-       d.volatileSet = volatileSet
-}
-
-// Add returns nil error as majority of devices don't need to do any host-side 
setup.
-func (d *deviceCommon) Add() error {
-       return nil
-}
-
-// Register returns nil error as majority of devices don't need to do any 
event registration.
-func (d *deviceCommon) Register() error {
-       return nil
-}
-
-// CanHotPlug returns true as majority of devices can be started/stopped when 
instance is running.
-// Also returns an empty list of update fields as most devices do not support 
live updates.
-func (d *deviceCommon) CanHotPlug() (bool, []string) {
-       return true, []string{}
-}
-
-// Update returns an error as most devices do not support live updates without 
being restarted.
-func (d *deviceCommon) Update(oldDevices deviceConfig.Devices, isRunning bool) 
error {
-       return fmt.Errorf("Device does not support updates whilst started")
-}
-
-// Remove returns nil error as majority of devices don't need to do any 
host-side cleanup on delete.
-func (d *deviceCommon) Remove() error {
-       return nil
-}
-
-// New instantiates a new device struct, validates the supplied config and 
sets it into the device.
-// If the device type is valid, but the other config validation fails then an 
instantiated device
-// is still returned with the validation error. If an unknown device is 
requested or the device is
-// not compatible with the instance type then an ErrUnsupportedDevType error 
is returned.
-func New(inst instance.Instance, state *state.State, name string, conf 
deviceConfig.Device, volatileGet VolatileGetter, volatileSet VolatileSetter) 
(Device, error) {
-       if conf["type"] == "" {
-               return nil, fmt.Errorf("Missing device type for device '%s'", 
name)
-       }
-
-       devFunc := devTypes[conf["type"]]
-
-       // Check if top-level type is recognised, if it is known type it will 
return a function.
-       if devFunc == nil {
-               return nil, ErrUnsupportedDevType
-       }
-
-       // Run the device create function and check it succeeds.
-       dev := devFunc(conf)
-       if dev == nil {
-               return nil, ErrUnsupportedDevType
-       }
-
-       // Init the device and run validation of supplied config.
-       dev.init(inst, state, name, conf, volatileGet, volatileSet)
-       err := dev.validateConfig()
-
-       // We still return the instantiated device here, as in some scenarios 
the caller
-       // may still want to use the device (such as when stopping or removing) 
even if
-       // the config validation has failed.
-       return dev, err
-}

From 9790359629cda3a0505274a8e916794891482f57 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 30 Jan 2020 13:25:58 +0000
Subject: [PATCH 03/12] lxd/device/device/interface: Splits device interfaces
 into own file

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/device/device_interface.go | 66 ++++++++++++++++++++++++++++++++++
 1 file changed, 66 insertions(+)
 create mode 100644 lxd/device/device_interface.go

diff --git a/lxd/device/device_interface.go b/lxd/device/device_interface.go
new file mode 100644
index 0000000000..52fc2edf89
--- /dev/null
+++ b/lxd/device/device_interface.go
@@ -0,0 +1,66 @@
+package device
+
+import (
+       deviceConfig "github.com/lxc/lxd/lxd/device/config"
+       "github.com/lxc/lxd/lxd/instance"
+       "github.com/lxc/lxd/lxd/state"
+)
+
+// VolatileSetter is a function that accepts one or more key/value strings to 
save into the LXD
+// config for this instance. It should add the volatile device name prefix to 
each key when saving.
+type VolatileSetter func(map[string]string) error
+
+// VolatileGetter is a function that retrieves any key/value string that 
exists in the LXD database
+// config for this instance. It should only return keys that match the 
volatile device name prefix,
+// and should remove the prefix before being returned.
+type VolatileGetter func() map[string]string
+
+// Device represents a device that can be added to an instance.
+type Device interface {
+       // CanHotPlug returns true if device can be managed whilst instance is 
running.
+       // It also returns a slice of config fields that can be live updated. 
If only fields in this
+       // list have changed then Update() is called rather than triggering a 
device remove & add.
+       CanHotPlug() (bool, []string)
+
+       // Add performs any host-side setup when a device is added to an 
instance.
+       // It is called irrespective of whether the instance is running or not.
+       Add() error
+
+       // Start peforms any host-side configuration required to start the 
device for the instance.
+       // This can be when a device is plugged into a running instance or the 
instance is starting.
+       // Returns run-time configuration needed for configuring the instance 
with the new device.
+       Start() (*deviceConfig.RunConfig, error)
+
+       // Register provides the ability for a device to subcribe to events 
that LXD can generate.
+       // It is called after a device is started (after Start()) or when LXD 
starts.
+       Register() error
+
+       // Update performs host-side modifications for a device based on the 
difference between the
+       // current config and previous devices config supplied as an argument. 
This called if the
+       // only config fields that have changed are supplied in the list 
returned from CanHotPlug().
+       // The function also accepts a boolean indicating whether the instance 
is running or not.
+       Update(oldDevices deviceConfig.Devices, running bool) error
+
+       // Stop performs any host-side cleanup required when a device is 
removed from an instance,
+       // either due to unplugging it from a running instance or instance is 
being shutdown.
+       // Returns run-time configuration needed for detaching the device from 
the instance.
+       Stop() (*deviceConfig.RunConfig, error)
+
+       // Remove performs any host-side cleanup when an instance is removed 
from an instance.
+       Remove() error
+}
+
+// device represents a sealed interface that implements Device, but also 
contains some internal
+// setup functions for a Device that should only be called by device.New() to 
avoid exposing devices
+// that are not in a known configured state. This is separate from the Device 
interface so that
+// Devices created outside of the device package can be used by LXD, but 
ensures that any devices
+// created by the device package will only be accessible after being 
configured properly by New().
+type device interface {
+       Device
+
+       // init stores the Instance, daemon State and Config into device and 
performs any setup.
+       init(instance.Instance, *state.State, string, deviceConfig.Device, 
VolatileGetter, VolatileSetter)
+
+       // validateConfig checks Config stored by init() is valid for the 
instance type.
+       validateConfig(instance.ConfigReader) error
+}

From 48c183cd0cee7fab25b872de295b06520928bd21 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 30 Jan 2020 13:27:10 +0000
Subject: [PATCH 04/12] lxd/device/device/load: Separates device load functions
 into own file

Adds Validate() function for validating devices without needing a concrete 
instance.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/device/device_load.go | 77 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 77 insertions(+)
 create mode 100644 lxd/device/device_load.go

diff --git a/lxd/device/device_load.go b/lxd/device/device_load.go
new file mode 100644
index 0000000000..1227dba7bb
--- /dev/null
+++ b/lxd/device/device_load.go
@@ -0,0 +1,77 @@
+package device
+
+import (
+       "fmt"
+
+       deviceConfig "github.com/lxc/lxd/lxd/device/config"
+       "github.com/lxc/lxd/lxd/instance"
+       "github.com/lxc/lxd/lxd/state"
+)
+
+// devTypes defines supported top-level device type creation functions.
+var devTypes = map[string]func(deviceConfig.Device) device{
+       "nic":          nicLoadByType,
+       "infiniband":   infinibandLoadByType,
+       "proxy":        func(c deviceConfig.Device) device { return &proxy{} },
+       "gpu":          func(c deviceConfig.Device) device { return &gpu{} },
+       "usb":          func(c deviceConfig.Device) device { return &usb{} },
+       "unix-char":    func(c deviceConfig.Device) device { return 
&unixCommon{} },
+       "unix-block":   func(c deviceConfig.Device) device { return 
&unixCommon{} },
+       "unix-hotplug": func(c deviceConfig.Device) device { return 
&unixHotplug{} },
+       "disk":         func(c deviceConfig.Device) device { return &disk{} },
+       "none":         func(c deviceConfig.Device) device { return &none{} },
+}
+
+// load instantiates a device and initialises its internal state. It does not 
validate the config supplied.
+func load(inst instance.Instance, state *state.State, name string, conf 
deviceConfig.Device, volatileGet VolatileGetter, volatileSet VolatileSetter) 
(device, error) {
+       if conf["type"] == "" {
+               return nil, fmt.Errorf("Missing device type for device %q", 
name)
+       }
+
+       devFunc := devTypes[conf["type"]]
+
+       // Check if top-level type is recognised, if it is known type it will 
return a function.
+       if devFunc == nil {
+               return nil, ErrUnsupportedDevType
+       }
+
+       // Run the device create function and check it succeeds.
+       dev := devFunc(conf)
+       if dev == nil {
+               return nil, ErrUnsupportedDevType
+       }
+
+       // Setup the device's internal variables.
+       dev.init(inst, state, name, conf, volatileGet, volatileSet)
+
+       return dev, nil
+}
+
+// New instantiates a new device struct, validates the supplied config and 
sets it into the device.
+// If the device type is valid, but the other config validation fails then an 
instantiated device
+// is still returned with the validation error. If an unknown device is 
requested or the device is
+// not compatible with the instance type then an ErrUnsupportedDevType error 
is returned.
+func New(inst instance.Instance, state *state.State, name string, conf 
deviceConfig.Device, volatileGet VolatileGetter, volatileSet VolatileSetter) 
(Device, error) {
+       dev, err := load(inst, state, name, conf, volatileGet, volatileSet)
+       if err != nil {
+               return nil, err
+       }
+
+       err = dev.validateConfig(inst)
+
+       // We still return the instantiated device here, as in some scenarios 
the caller
+       // may still want to use the device (such as when stopping or removing) 
even if
+       // the config validation has failed.
+       return dev, err
+}
+
+// Validate checks a device's config is valid. This only requires an 
instance.ConfigReader rather than an full
+// blown instance to allow profile devices to be validated too.
+func Validate(instConfig instance.ConfigReader, state *state.State, name 
string, conf deviceConfig.Device) error {
+       dev, err := load(nil, state, name, conf, nil, nil)
+       if err != nil {
+               return err
+       }
+
+       return dev.validateConfig(instConfig)
+}

From 7c6e99090383a62ed3d7ea50e6e167f8a287b942 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 30 Jan 2020 13:27:53 +0000
Subject: [PATCH 05/12] lxd/instance/drivers/driver/common: Adds common driver
 type

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/instance/drivers/driver_common.go | 74 +++++++++++++++++++++++++++
 1 file changed, 74 insertions(+)
 create mode 100644 lxd/instance/drivers/driver_common.go

diff --git a/lxd/instance/drivers/driver_common.go 
b/lxd/instance/drivers/driver_common.go
new file mode 100644
index 0000000000..10d70c9dc1
--- /dev/null
+++ b/lxd/instance/drivers/driver_common.go
@@ -0,0 +1,74 @@
+package drivers
+
+import (
+       "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/state"
+       "github.com/lxc/lxd/shared/api"
+)
+
+// common provides structure common to all instance types.
+type common struct {
+       dbType          instancetype.Type
+       expandedConfig  map[string]string
+       expandedDevices deviceConfig.Devices
+       localConfig     map[string]string
+       localDevices    deviceConfig.Devices
+       profiles        []string
+       project         string
+       state           *state.State
+}
+
+// Type returns the instance's type.
+func (c *common) Type() instancetype.Type {
+       return c.dbType
+}
+
+// ExpandedConfig returns instance's expanded config.
+func (c *common) ExpandedConfig() map[string]string {
+       return c.expandedConfig
+}
+
+// ExpandedDevices returns instance's expanded device config.
+func (c *common) ExpandedDevices() deviceConfig.Devices {
+       return c.expandedDevices
+}
+
+// LocalConfig returns the instance's local config.
+func (c *common) LocalConfig() map[string]string {
+       return c.localConfig
+}
+
+// LocalDevices returns the instance's local device config.
+func (c *common) LocalDevices() deviceConfig.Devices {
+       return c.localDevices
+}
+
+func (c *common) expandConfig(profiles []api.Profile) error {
+       if profiles == nil && len(c.profiles) > 0 {
+               var err error
+               profiles, err = c.state.Cluster.ProfilesGet(c.project, 
c.profiles)
+               if err != nil {
+                       return err
+               }
+       }
+
+       c.expandedConfig = db.ProfilesExpandConfig(c.localConfig, profiles)
+
+       return nil
+}
+
+func (c *common) expandDevices(profiles []api.Profile) error {
+       if profiles == nil && len(c.profiles) > 0 {
+               var err error
+               profiles, err = c.state.Cluster.ProfilesGet(c.project, 
c.profiles)
+               if err != nil {
+                       return err
+               }
+       }
+
+       c.expandedDevices = db.ProfilesExpandDevices(c.localDevices, profiles)
+
+       return nil
+}

From 076616edde0ac55737fb6086a26b25e2b9b337ce Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 30 Jan 2020 13:28:45 +0000
Subject: [PATCH 06/12] lxd/instance/instance/interface: Adds ConfigRead
 interface

For accessing an instance's type and config only.

Embeds this into existing Instance interface.

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

diff --git a/lxd/instance/instance_interface.go 
b/lxd/instance/instance_interface.go
index 59a39167ad..1018ddf780 100644
--- a/lxd/instance/instance_interface.go
+++ b/lxd/instance/instance_interface.go
@@ -14,8 +14,19 @@ import (
        "github.com/lxc/lxd/shared/api"
 )
 
+// ConfigReader is used to read instance config.
+type ConfigReader interface {
+       Type() instancetype.Type
+       ExpandedConfig() map[string]string
+       ExpandedDevices() deviceConfig.Devices
+       LocalConfig() map[string]string
+       LocalDevices() deviceConfig.Devices
+}
+
 // The Instance interface.
 type Instance interface {
+       ConfigReader
+
        // Instance actions.
        Freeze() error
        Shutdown(timeout time.Duration) error
@@ -71,15 +82,11 @@ type Instance interface {
        Location() string
        Project() string
        Name() string
-       Type() instancetype.Type
        Description() string
        Architecture() int
        CreationDate() time.Time
        LastUsedDate() time.Time
-       ExpandedConfig() map[string]string
-       ExpandedDevices() deviceConfig.Devices
-       LocalConfig() map[string]string
-       LocalDevices() deviceConfig.Devices
+
        Profiles() []string
        InitPID() int
        State() string

From e319b22496359e2718cd6ff3043b7eee8b63ead6 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 30 Jan 2020 13:30:00 +0000
Subject: [PATCH 07/12] lxd/instance/drivers/load: Updates validDevices() to
 use device.Validate function

This avoids needing to instantiate a concrete instance type for profile 
validation.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/instance/drivers/load.go | 27 +++++++--------------------
 1 file changed, 7 insertions(+), 20 deletions(-)

diff --git a/lxd/instance/drivers/load.go b/lxd/instance/drivers/load.go
index 018ad0fb34..a77e55f8a0 100644
--- a/lxd/instance/drivers/load.go
+++ b/lxd/instance/drivers/load.go
@@ -47,40 +47,27 @@ func load(s *state.State, args db.InstanceArgs, profiles 
[]api.Profile) (instanc
 }
 
 // validDevices validate instance device configs.
-func validDevices(state *state.State, cluster *db.Cluster, instanceType 
instancetype.Type, instanceName string, devices deviceConfig.Devices, expanded 
bool) error {
+func validDevices(state *state.State, cluster *db.Cluster, instanceType 
instancetype.Type, devices deviceConfig.Devices, expanded bool) error {
        // Empty device list
        if devices == nil {
                return nil
        }
 
-       // Create temporary InstanceArgs, populate it's name, localDevices and 
expandedDevices properties based
-       // on the mode of validation occurring. In non-expanded validation 
expensive checks should be avoided.
-       instArgs := db.InstanceArgs{
-               Name:    instanceName,
-               Type:    instanceType,
-               Devices: devices.Clone(), // Prevent devices from modifying 
their config.
+       instConf := &common{
+               dbType:       instanceType,
+               localDevices: devices.Clone(),
        }
 
-       var expandedDevices deviceConfig.Devices
+       // In non-expanded validation expensive checks should be avoided.
        if expanded {
                // The devices being validated are already expanded, so just 
use the same
                // devices clone as we used for the main devices config.
-               expandedDevices = instArgs.Devices
-       }
-
-       // Create a temporary Instance for use in device validation.
-       var inst instance.Instance
-       if instArgs.Type == instancetype.Container {
-               inst = LXCInstantiate(state, instArgs, expandedDevices)
-       } else if instArgs.Type == instancetype.VM {
-               inst = qemuInstantiate(state, instArgs, expandedDevices)
-       } else {
-               return fmt.Errorf("Invalid instance type")
+               instConf.expandedDevices = instConf.localDevices
        }
 
        // Check each device individually using the device package.
        for name, config := range devices {
-               _, err := device.New(inst, state, name, config, nil, nil)
+               err := device.Validate(instConf, state, name, config)
                if err != nil {
                        return errors.Wrapf(err, "Device validation failed %q", 
name)
                }

From feb032e1dc5b924ea790b07aaee9d25dc0c945e2 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 30 Jan 2020 13:30:41 +0000
Subject: [PATCH 08/12] lxd/instance/instance/utils: Removes instanceName from
 validateDevices function

Reinforces that device validation doesn't need instance name, as we might be 
validating profile devices.

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

diff --git a/lxd/instance/instance_utils.go b/lxd/instance/instance_utils.go
index 9dbc6e8d1c..b418f90dac 100644
--- a/lxd/instance/instance_utils.go
+++ b/lxd/instance/instance_utils.go
@@ -32,11 +32,8 @@ 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 instance/drivers.validDevices to validate 
device config.
-var ValidDevices func(state *state.State, cluster *db.Cluster, instanceType 
instancetype.Type, instanceName string, devices deviceConfig.Devices, expanded 
bool) error
+var ValidDevices func(state *state.State, cluster *db.Cluster, instanceType 
instancetype.Type, devices deviceConfig.Devices, expanded bool) error
 
 // Load is linked from instance/drivers.load to allow different instance types 
to be loaded.
 var Load func(s *state.State, args db.InstanceArgs, profiles []api.Profile) 
(Instance, error)

From 451c7509242e5142c4dc187fa9e697f715235d28 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 30 Jan 2020 13:33:09 +0000
Subject: [PATCH 09/12] lxd/instance/drivers/driver/qemu: Embeds common type
 and removes dupe functionality

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/instance/drivers/driver_qemu.go | 101 ++++++----------------------
 1 file changed, 21 insertions(+), 80 deletions(-)

diff --git a/lxd/instance/drivers/driver_qemu.go 
b/lxd/instance/drivers/driver_qemu.go
index 556b1bc32a..f816561b0a 100644
--- a/lxd/instance/drivers/driver_qemu.go
+++ b/lxd/instance/drivers/driver_qemu.go
@@ -32,7 +32,6 @@ import (
        deviceConfig "github.com/lxc/lxd/lxd/device/config"
        "github.com/lxc/lxd/lxd/instance"
        "github.com/lxc/lxd/lxd/instance/drivers/qmp"
-       "github.com/lxc/lxd/lxd/instance/instancetype"
        "github.com/lxc/lxd/lxd/instance/operationlock"
        "github.com/lxc/lxd/lxd/maas"
        "github.com/lxc/lxd/lxd/operations"
@@ -84,20 +83,22 @@ func qemuLoad(s *state.State, args db.InstanceArgs, 
profiles []api.Profile) (ins
 // have access to the profiles used to do it. This can be safely passed as nil 
if not required.
 func qemuInstantiate(s *state.State, args db.InstanceArgs, expandedDevices 
deviceConfig.Devices) *qemu {
        vm := &qemu{
-               state:        s,
+               common: common{
+                       dbType:       args.Type,
+                       localConfig:  args.Config,
+                       localDevices: args.Devices,
+                       project:      args.Project,
+                       state:        s,
+                       profiles:     args.Profiles,
+               },
                id:           args.ID,
-               project:      args.Project,
                name:         args.Name,
                description:  args.Description,
                ephemeral:    args.Ephemeral,
                architecture: args.Architecture,
-               dbType:       args.Type,
                snapshot:     args.Snapshot,
                creationDate: args.CreationDate,
                lastUsedDate: args.LastUsedDate,
-               profiles:     args.Profiles,
-               localConfig:  args.Config,
-               localDevices: args.Devices,
                stateful:     args.Stateful,
                node:         args.Node,
                expiryDate:   args.ExpiryDate,
@@ -128,22 +129,24 @@ func qemuInstantiate(s *state.State, args 
db.InstanceArgs, expandedDevices devic
 func qemuCreate(s *state.State, args db.InstanceArgs) (instance.Instance, 
error) {
        // Create the instance struct.
        vm := &qemu{
-               state:        s,
+               common: common{
+                       dbType:       args.Type,
+                       localConfig:  args.Config,
+                       localDevices: args.Devices,
+                       state:        s,
+                       profiles:     args.Profiles,
+                       project:      args.Project,
+               },
                id:           args.ID,
-               project:      args.Project,
                name:         args.Name,
                node:         args.Node,
                description:  args.Description,
                ephemeral:    args.Ephemeral,
                architecture: args.Architecture,
-               dbType:       args.Type,
                snapshot:     args.Snapshot,
                stateful:     args.Stateful,
                creationDate: args.CreationDate,
                lastUsedDate: args.LastUsedDate,
-               profiles:     args.Profiles,
-               localConfig:  args.Config,
-               localDevices: args.Devices,
                expiryDate:   args.ExpiryDate,
        }
 
@@ -191,7 +194,7 @@ func qemuCreate(s *state.State, args db.InstanceArgs) 
(instance.Instance, error)
                return nil, err
        }
 
-       err = instance.ValidDevices(s, s.Cluster, vm.Type(), vm.Name(), 
vm.expandedDevices, true)
+       err = instance.ValidDevices(s, s.Cluster, vm.Type(), 
vm.expandedDevices, true)
        if err != nil {
                logger.Error("Failed creating instance", ctxMap)
                return nil, errors.Wrap(err, "Invalid devices")
@@ -255,28 +258,19 @@ func qemuCreate(s *state.State, args db.InstanceArgs) 
(instance.Instance, error)
 
 // qemu is the QEMU virtual machine driver.
 type qemu struct {
+       common
+
        // Properties.
        architecture int
-       dbType       instancetype.Type
        snapshot     bool
        creationDate time.Time
        lastUsedDate time.Time
        ephemeral    bool
        id           int
-       project      string
        name         string
        description  string
        stateful     bool
 
-       // Config.
-       expandedConfig  map[string]string
-       expandedDevices deviceConfig.Devices
-       localConfig     map[string]string
-       localDevices    deviceConfig.Devices
-       profiles        []string
-
-       state *state.State
-
        // Clustering.
        node string
 
@@ -1977,7 +1971,7 @@ func (vm *qemu) Update(args db.InstanceArgs, 
userRequested bool) error {
        }
 
        // Validate the new devices without using expanded devices validation 
(expensive checks disabled).
-       err = instance.ValidDevices(vm.state, vm.state.Cluster, vm.Type(), 
vm.Name(), args.Devices, false)
+       err = instance.ValidDevices(vm.state, vm.state.Cluster, vm.Type(), 
args.Devices, false)
        if err != nil {
                return errors.Wrap(err, "Invalid devices")
        }
@@ -2161,7 +2155,7 @@ func (vm *qemu) Update(args db.InstanceArgs, 
userRequested bool) error {
        }
 
        // Do full expanded validation of the devices diff.
-       err = instance.ValidDevices(vm.state, vm.state.Cluster, vm.Type(), 
vm.Name(), vm.expandedDevices, true)
+       err = instance.ValidDevices(vm.state, vm.state.Cluster, vm.Type(), 
vm.expandedDevices, true)
        if err != nil {
                return errors.Wrap(err, "Invalid expanded devices")
        }
@@ -3265,11 +3259,6 @@ func (vm *qemu) Name() string {
        return vm.name
 }
 
-// Type returns the instance's type.
-func (vm *qemu) Type() instancetype.Type {
-       return vm.dbType
-}
-
 // Description returns the instance's description.
 func (vm *qemu) Description() string {
        return vm.description
@@ -3290,54 +3279,6 @@ func (vm *qemu) LastUsedDate() time.Time {
        return vm.lastUsedDate
 }
 
-func (vm *qemu) expandConfig(profiles []api.Profile) error {
-       if profiles == nil && len(vm.profiles) > 0 {
-               var err error
-               profiles, err = vm.state.Cluster.ProfilesGet(vm.project, 
vm.profiles)
-               if err != nil {
-                       return err
-               }
-       }
-
-       vm.expandedConfig = db.ProfilesExpandConfig(vm.localConfig, profiles)
-
-       return nil
-}
-
-func (vm *qemu) expandDevices(profiles []api.Profile) error {
-       if profiles == nil && len(vm.profiles) > 0 {
-               var err error
-               profiles, err = vm.state.Cluster.ProfilesGet(vm.project, 
vm.profiles)
-               if err != nil {
-                       return err
-               }
-       }
-
-       vm.expandedDevices = db.ProfilesExpandDevices(vm.localDevices, profiles)
-
-       return nil
-}
-
-// ExpandedConfig returns instance's expanded config.
-func (vm *qemu) ExpandedConfig() map[string]string {
-       return vm.expandedConfig
-}
-
-// ExpandedDevices returns instance's expanded device config.
-func (vm *qemu) ExpandedDevices() deviceConfig.Devices {
-       return vm.expandedDevices
-}
-
-// LocalConfig returns the instance's local config.
-func (vm *qemu) LocalConfig() map[string]string {
-       return vm.localConfig
-}
-
-// LocalDevices returns the instance's local device config.
-func (vm *qemu) LocalDevices() deviceConfig.Devices {
-       return vm.localDevices
-}
-
 // Profiles returns the instance's profiles.
 func (vm *qemu) Profiles() []string {
        return vm.profiles

From a7aa674072371bff94719b417c2b5817705f126b Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 30 Jan 2020 13:33:58 +0000
Subject: [PATCH 10/12] lxd: instance.ValidDevices usage

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

diff --git a/lxd/container.go b/lxd/container.go
index 844fe79b75..5283d13ec3 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -689,7 +689,7 @@ func instanceCreateInternal(s *state.State, args 
db.InstanceArgs) (instance.Inst
        }
 
        // Validate container devices with the supplied container name and 
devices.
-       err = instance.ValidDevices(s, s.Cluster, args.Type, args.Name, 
args.Devices, false)
+       err = instance.ValidDevices(s, s.Cluster, args.Type, 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 6e162807b4..95a6a939fc 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -205,7 +205,7 @@ func containerLXCCreate(s *state.State, args 
db.InstanceArgs) (instance.Instance
                return nil, err
        }
 
-       err = instance.ValidDevices(s, s.Cluster, c.Type(), c.Name(), 
c.expandedDevices, true)
+       err = instance.ValidDevices(s, s.Cluster, c.Type(), c.expandedDevices, 
true)
        if err != nil {
                c.Delete()
                logger.Error("Failed creating container", ctxMap)
@@ -3922,7 +3922,7 @@ func (c *containerLXC) Update(args db.InstanceArgs, 
userRequested bool) error {
        }
 
        // Validate the new devices without using expanded devices validation 
(expensive checks disabled).
-       err = instance.ValidDevices(c.state, c.state.Cluster, c.Type(), 
c.Name(), args.Devices, false)
+       err = instance.ValidDevices(c.state, c.state.Cluster, c.Type(), 
args.Devices, false)
        if err != nil {
                return errors.Wrap(err, "Invalid devices")
        }
@@ -4113,7 +4113,7 @@ func (c *containerLXC) Update(args db.InstanceArgs, 
userRequested bool) error {
        }
 
        // Do full expanded validation of the devices diff.
-       err = instance.ValidDevices(c.state, c.state.Cluster, c.Type(), 
c.Name(), c.expandedDevices, true)
+       err = instance.ValidDevices(c.state, c.state.Cluster, c.Type(), 
c.expandedDevices, true)
        if err != nil {
                return errors.Wrap(err, "Invalid expanded devices")
        }
diff --git a/lxd/profiles.go b/lxd/profiles.go
index df37752ecb..590a8df759 100644
--- a/lxd/profiles.go
+++ b/lxd/profiles.go
@@ -109,9 +109,8 @@ func profilesPost(d *Daemon, r *http.Request) 
response.Response {
                return response.BadRequest(err)
        }
 
-       // 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 = instance.ValidDevices(d.State(), d.cluster, 
instancetype.Container, instance.ProfileValidationName, 
deviceConfig.NewDevices(req.Devices), false)
+       // At this point we don't know the instance type, so just use 
instancetype.Any type for validation.
+       err = instance.ValidDevices(d.State(), d.cluster, instancetype.Any, 
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 b2e00869e4..a2e050fed7 100644
--- a/lxd/profiles_utils.go
+++ b/lxd/profiles_utils.go
@@ -21,9 +21,8 @@ func doProfileUpdate(d *Daemon, project, name string, id 
int64, profile *api.Pro
                return err
        }
 
-       // 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 = instance.ValidDevices(d.State(), d.cluster, 
instancetype.Container, instance.ProfileValidationName, 
deviceConfig.NewDevices(req.Devices), false)
+       // At this point we don't know the instance type, so just use 
instancetype.Any type for validation.
+       err = instance.ValidDevices(d.State(), d.cluster, instancetype.Any, 
deviceConfig.NewDevices(req.Devices), false)
        if err != nil {
                return err
        }

From 2a0af09f06482bda5fbf9b6d9fd7d57c6204b4eb Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 30 Jan 2020 13:44:49 +0000
Subject: [PATCH 11/12] lxd/device/device/utils/instance: Adds
 instanceSupported function

Helper function to check instance type matches instancetype.Any or one of the 
instance types supplied.

The instancetype.Any is always supported to allow mixed-instance type profile 
validation to be supported.

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

diff --git a/lxd/device/device_utils_instance.go 
b/lxd/device/device_utils_instance.go
index d001461ee5..2d808c3055 100644
--- a/lxd/device/device_utils_instance.go
+++ b/lxd/device/device_utils_instance.go
@@ -6,6 +6,7 @@ import (
 
        deviceConfig "github.com/lxc/lxd/lxd/device/config"
        "github.com/lxc/lxd/lxd/instance"
+       "github.com/lxc/lxd/lxd/instance/instancetype"
        "github.com/lxc/lxd/lxd/state"
 )
 
@@ -54,3 +55,20 @@ func instanceGetReservedDevices(s *state.State, m 
deviceConfig.Device) (map[stri
 
        return reservedDevices, nil
 }
+
+// instanceSupported is a helper function to check instance type is supported 
for validation.
+// Always returns true if supplied instance type is Any, to support profile 
validation.
+func instanceSupported(instType instancetype.Type, supportedTypes 
...instancetype.Type) bool {
+       // If instance type is Any, then profile validation is occurring and we 
need to support this.
+       if instType == instancetype.Any {
+               return true
+       }
+
+       for _, supportedType := range supportedTypes {
+               if instType == supportedType {
+                       return true
+               }
+       }
+
+       return false
+}

From 0ba74d0e96a4b2f004ad174bcf84d1c4f304316b Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 30 Jan 2020 13:46:18 +0000
Subject: [PATCH 12/12] lxd/device: Updates device validateConfig to support
 instance.ConfigReader argument

Switches validation implementations to use instance.ConfigReader argument so 
profile validation can occur without a concrete instance being supplied.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/device/disk.go                | 19 +++++++++----------
 lxd/device/gpu.go                 |  5 +++--
 lxd/device/infiniband_physical.go |  5 +++--
 lxd/device/infiniband_sriov.go    |  5 +++--
 lxd/device/nic_bridged.go         |  5 +++--
 lxd/device/nic_ipvlan.go          |  5 +++--
 lxd/device/nic_macvlan.go         |  5 +++--
 lxd/device/nic_p2p.go             |  5 +++--
 lxd/device/nic_physical.go        |  7 ++++---
 lxd/device/nic_routed.go          |  5 +++--
 lxd/device/nic_sriov.go           |  7 ++++---
 lxd/device/none.go                |  3 ++-
 lxd/device/proxy.go               |  5 +++--
 lxd/device/unix_common.go         |  5 +++--
 lxd/device/unix_hotplug.go        |  5 +++--
 lxd/device/usb.go                 |  5 +++--
 16 files changed, 55 insertions(+), 41 deletions(-)

diff --git a/lxd/device/disk.go b/lxd/device/disk.go
index f2b4eb00c6..ada9b9d2a2 100644
--- a/lxd/device/disk.go
+++ b/lxd/device/disk.go
@@ -49,8 +49,8 @@ func (d *disk) isRequired(devConfig deviceConfig.Device) bool 
{
 }
 
 // validateConfig checks the supplied config for correctness.
-func (d *disk) validateConfig() error {
-       if d.inst.Type() != instancetype.Container && d.inst.Type() != 
instancetype.VM {
+func (d *disk) validateConfig(instConf instance.ConfigReader) error {
+       if !instanceSupported(instConf.Type(), instancetype.Container, 
instancetype.VM) {
                return ErrUnsupportedDevType
        }
 
@@ -87,9 +87,9 @@ func (d *disk) validateConfig() error {
 
        // 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 {
+       if instConf.Type() == instancetype.Any {
                rules["path"] = shared.IsAny
-       } else if d.inst.Type() == instancetype.Container || d.config["path"] 
== "/" {
+       } else if instConf.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
        }
@@ -133,7 +133,7 @@ func (d *disk) validateConfig() error {
        // same path, so that if merged profiles share the same the path and 
then one is removed
        // this can still be cleanly removed.
        pathCount := 0
-       for _, devConfig := range d.inst.LocalDevices() {
+       for _, devConfig := range instConf.LocalDevices() {
                if devConfig["type"] == "disk" && d.config["path"] != "" && 
devConfig["path"] == d.config["path"] {
                        pathCount++
                        if pathCount > 1 {
@@ -165,11 +165,10 @@ func (d *disk) validateConfig() error {
                        return fmt.Errorf("The \"%s\" storage pool doesn't 
exist", d.config["pool"])
                }
 
-               // Only check storate volume is available if we are validating 
an instance device
-               // 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() != instance.ProfileValidationName && 
len(d.inst.ExpandedDevices()) > 0 && d.config["source"] != "" && 
d.config["path"] != "/" {
+               // Only check storate volume is available if we are validating 
an instance device and not a profile
+               // device (check for instancetype.Any), and we have least one 
expanded device (this is so we only
+               // do this expensive check after devices have been expanded).
+               if instConf.Type() != instancetype.Any && 
len(instConf.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)
diff --git a/lxd/device/gpu.go b/lxd/device/gpu.go
index 30c5888040..9bb4c39c71 100644
--- a/lxd/device/gpu.go
+++ b/lxd/device/gpu.go
@@ -12,6 +12,7 @@ import (
        "golang.org/x/sys/unix"
 
        deviceConfig "github.com/lxc/lxd/lxd/device/config"
+       "github.com/lxc/lxd/lxd/instance"
        "github.com/lxc/lxd/lxd/instance/instancetype"
        "github.com/lxc/lxd/lxd/resources"
        "github.com/lxc/lxd/shared"
@@ -31,8 +32,8 @@ type gpu struct {
 }
 
 // validateConfig checks the supplied config for correctness.
-func (d *gpu) validateConfig() error {
-       if d.inst.Type() != instancetype.Container {
+func (d *gpu) validateConfig(instConf instance.ConfigReader) error {
+       if !instanceSupported(instConf.Type(), instancetype.Container) {
                return ErrUnsupportedDevType
        }
 
diff --git a/lxd/device/infiniband_physical.go 
b/lxd/device/infiniband_physical.go
index 482f39d817..2c0edefbc6 100644
--- a/lxd/device/infiniband_physical.go
+++ b/lxd/device/infiniband_physical.go
@@ -4,6 +4,7 @@ import (
        "fmt"
 
        deviceConfig "github.com/lxc/lxd/lxd/device/config"
+       "github.com/lxc/lxd/lxd/instance"
        "github.com/lxc/lxd/lxd/instance/instancetype"
        "github.com/lxc/lxd/lxd/resources"
        "github.com/lxc/lxd/shared"
@@ -14,8 +15,8 @@ type infinibandPhysical struct {
 }
 
 // validateConfig checks the supplied config for correctness.
-func (d *infinibandPhysical) validateConfig() error {
-       if d.inst.Type() != instancetype.Container {
+func (d *infinibandPhysical) validateConfig(instConf instance.ConfigReader) 
error {
+       if !instanceSupported(instConf.Type(), instancetype.Container) {
                return ErrUnsupportedDevType
        }
 
diff --git a/lxd/device/infiniband_sriov.go b/lxd/device/infiniband_sriov.go
index b6932e9753..305967a032 100644
--- a/lxd/device/infiniband_sriov.go
+++ b/lxd/device/infiniband_sriov.go
@@ -4,6 +4,7 @@ import (
        "fmt"
 
        deviceConfig "github.com/lxc/lxd/lxd/device/config"
+       "github.com/lxc/lxd/lxd/instance"
        "github.com/lxc/lxd/lxd/instance/instancetype"
        "github.com/lxc/lxd/lxd/resources"
        "github.com/lxc/lxd/shared"
@@ -15,8 +16,8 @@ type infinibandSRIOV struct {
 }
 
 // validateConfig checks the supplied config for correctness.
-func (d *infinibandSRIOV) validateConfig() error {
-       if d.inst.Type() != instancetype.Container {
+func (d *infinibandSRIOV) validateConfig(instConf instance.ConfigReader) error 
{
+       if !instanceSupported(instConf.Type(), instancetype.Container) {
                return ErrUnsupportedDevType
        }
 
diff --git a/lxd/device/nic_bridged.go b/lxd/device/nic_bridged.go
index 699caddf5a..eb93964ffd 100644
--- a/lxd/device/nic_bridged.go
+++ b/lxd/device/nic_bridged.go
@@ -23,6 +23,7 @@ import (
        deviceConfig "github.com/lxc/lxd/lxd/device/config"
        "github.com/lxc/lxd/lxd/dnsmasq"
        firewallConsts "github.com/lxc/lxd/lxd/firewall/consts"
+       "github.com/lxc/lxd/lxd/instance"
        "github.com/lxc/lxd/lxd/instance/instancetype"
        "github.com/lxc/lxd/lxd/util"
        "github.com/lxc/lxd/shared"
@@ -48,8 +49,8 @@ type nicBridged struct {
 }
 
 // validateConfig checks the supplied config for correctness.
-func (d *nicBridged) validateConfig() error {
-       if d.inst.Type() != instancetype.Container && d.inst.Type() != 
instancetype.VM {
+func (d *nicBridged) validateConfig(instConf instance.ConfigReader) error {
+       if !instanceSupported(instConf.Type(), instancetype.Container, 
instancetype.VM) {
                return ErrUnsupportedDevType
        }
 
diff --git a/lxd/device/nic_ipvlan.go b/lxd/device/nic_ipvlan.go
index 75c8227f8f..92ae30174e 100644
--- a/lxd/device/nic_ipvlan.go
+++ b/lxd/device/nic_ipvlan.go
@@ -5,6 +5,7 @@ import (
        "strings"
 
        deviceConfig "github.com/lxc/lxd/lxd/device/config"
+       "github.com/lxc/lxd/lxd/instance"
        "github.com/lxc/lxd/lxd/instance/instancetype"
        "github.com/lxc/lxd/lxd/util"
        "github.com/lxc/lxd/shared"
@@ -19,8 +20,8 @@ func (d *nicIPVLAN) CanHotPlug() (bool, []string) {
 }
 
 // validateConfig checks the supplied config for correctness.
-func (d *nicIPVLAN) validateConfig() error {
-       if d.inst.Type() != instancetype.Container {
+func (d *nicIPVLAN) validateConfig(instConf instance.ConfigReader) error {
+       if !instanceSupported(instConf.Type(), instancetype.Container) {
                return ErrUnsupportedDevType
        }
 
diff --git a/lxd/device/nic_macvlan.go b/lxd/device/nic_macvlan.go
index 03d34a9e47..46d67dd429 100644
--- a/lxd/device/nic_macvlan.go
+++ b/lxd/device/nic_macvlan.go
@@ -4,6 +4,7 @@ import (
        "fmt"
 
        deviceConfig "github.com/lxc/lxd/lxd/device/config"
+       "github.com/lxc/lxd/lxd/instance"
        "github.com/lxc/lxd/lxd/instance/instancetype"
        "github.com/lxc/lxd/lxd/revert"
        "github.com/lxc/lxd/shared"
@@ -14,8 +15,8 @@ type nicMACVLAN struct {
 }
 
 // validateConfig checks the supplied config for correctness.
-func (d *nicMACVLAN) validateConfig() error {
-       if d.inst.Type() != instancetype.Container && d.inst.Type() != 
instancetype.VM {
+func (d *nicMACVLAN) validateConfig(instConf instance.ConfigReader) error {
+       if !instanceSupported(instConf.Type(), instancetype.Container, 
instancetype.VM) {
                return ErrUnsupportedDevType
        }
 
diff --git a/lxd/device/nic_p2p.go b/lxd/device/nic_p2p.go
index 2043597110..e0882a8b27 100644
--- a/lxd/device/nic_p2p.go
+++ b/lxd/device/nic_p2p.go
@@ -4,6 +4,7 @@ import (
        "fmt"
 
        deviceConfig "github.com/lxc/lxd/lxd/device/config"
+       "github.com/lxc/lxd/lxd/instance"
        "github.com/lxc/lxd/lxd/instance/instancetype"
        "github.com/lxc/lxd/shared"
 )
@@ -13,8 +14,8 @@ type nicP2P struct {
 }
 
 // validateConfig checks the supplied config for correctness.
-func (d *nicP2P) validateConfig() error {
-       if d.inst.Type() != instancetype.Container && d.inst.Type() != 
instancetype.VM {
+func (d *nicP2P) validateConfig(instConf instance.ConfigReader) error {
+       if !instanceSupported(instConf.Type(), instancetype.Container, 
instancetype.VM) {
                return ErrUnsupportedDevType
        }
 
diff --git a/lxd/device/nic_physical.go b/lxd/device/nic_physical.go
index b8d4662b72..f8627373d7 100644
--- a/lxd/device/nic_physical.go
+++ b/lxd/device/nic_physical.go
@@ -6,6 +6,7 @@ import (
        "github.com/pkg/errors"
 
        deviceConfig "github.com/lxc/lxd/lxd/device/config"
+       "github.com/lxc/lxd/lxd/instance"
        "github.com/lxc/lxd/lxd/instance/instancetype"
        "github.com/lxc/lxd/lxd/revert"
        "github.com/lxc/lxd/lxd/util"
@@ -17,8 +18,8 @@ type nicPhysical struct {
 }
 
 // validateConfig checks the supplied config for correctness.
-func (d *nicPhysical) validateConfig() error {
-       if d.inst.Type() != instancetype.Container && d.inst.Type() != 
instancetype.VM {
+func (d *nicPhysical) validateConfig(instConf instance.ConfigReader) error {
+       if !instanceSupported(instConf.Type(), instancetype.Container, 
instancetype.VM) {
                return ErrUnsupportedDevType
        }
 
@@ -30,7 +31,7 @@ func (d *nicPhysical) validateConfig() error {
                "boot.priority",
        }
 
-       if d.inst.Type() == instancetype.Container {
+       if instConf.Type() == instancetype.Container {
                optionalFields = append(optionalFields, "mtu", "hwaddr", "vlan")
        }
 
diff --git a/lxd/device/nic_routed.go b/lxd/device/nic_routed.go
index 6077ce41bc..6198c1fb9c 100644
--- a/lxd/device/nic_routed.go
+++ b/lxd/device/nic_routed.go
@@ -5,6 +5,7 @@ import (
        "strings"
 
        deviceConfig "github.com/lxc/lxd/lxd/device/config"
+       "github.com/lxc/lxd/lxd/instance"
        "github.com/lxc/lxd/lxd/instance/instancetype"
        "github.com/lxc/lxd/lxd/util"
        "github.com/lxc/lxd/shared"
@@ -22,8 +23,8 @@ func (d *nicRouted) CanHotPlug() (bool, []string) {
 }
 
 // validateConfig checks the supplied config for correctness.
-func (d *nicRouted) validateConfig() error {
-       if d.inst.Type() != instancetype.Container {
+func (d *nicRouted) validateConfig(instConf instance.ConfigReader) error {
+       if !instanceSupported(instConf.Type(), instancetype.Container) {
                return ErrUnsupportedDevType
        }
 
diff --git a/lxd/device/nic_sriov.go b/lxd/device/nic_sriov.go
index 9269604381..572670616e 100644
--- a/lxd/device/nic_sriov.go
+++ b/lxd/device/nic_sriov.go
@@ -11,6 +11,7 @@ import (
        "strings"
 
        deviceConfig "github.com/lxc/lxd/lxd/device/config"
+       "github.com/lxc/lxd/lxd/instance"
        "github.com/lxc/lxd/lxd/instance/instancetype"
        "github.com/lxc/lxd/lxd/revert"
        "github.com/lxc/lxd/shared"
@@ -21,8 +22,8 @@ type nicSRIOV struct {
 }
 
 // validateConfig checks the supplied config for correctness.
-func (d *nicSRIOV) validateConfig() error {
-       if d.inst.Type() != instancetype.Container && d.inst.Type() != 
instancetype.VM {
+func (d *nicSRIOV) validateConfig(instConf instance.ConfigReader) error {
+       if !instanceSupported(instConf.Type(), instancetype.Container, 
instancetype.VM) {
                return ErrUnsupportedDevType
        }
 
@@ -38,7 +39,7 @@ func (d *nicSRIOV) validateConfig() error {
        }
 
        // For VMs only NIC properties that can be specified on the parent's VF 
settings are controllable.
-       if d.inst.Type() == instancetype.Container {
+       if instConf.Type() == instancetype.Container {
                optionalFields = append(optionalFields, "mtu")
        }
 
diff --git a/lxd/device/none.go b/lxd/device/none.go
index 44026a1933..dcd2a70dba 100644
--- a/lxd/device/none.go
+++ b/lxd/device/none.go
@@ -2,6 +2,7 @@ package device
 
 import (
        deviceConfig "github.com/lxc/lxd/lxd/device/config"
+       "github.com/lxc/lxd/lxd/instance"
 )
 
 type none struct {
@@ -9,7 +10,7 @@ type none struct {
 }
 
 // validateConfig checks the supplied config for correctness.
-func (d *none) validateConfig() error {
+func (d *none) validateConfig(instConf instance.ConfigReader) error {
        rules := map[string]func(string) error{} // No fields allowed.
        err := d.config.Validate(rules)
        if err != nil {
diff --git a/lxd/device/proxy.go b/lxd/device/proxy.go
index 1ac382907c..77a1394575 100644
--- a/lxd/device/proxy.go
+++ b/lxd/device/proxy.go
@@ -17,6 +17,7 @@ import (
 
        deviceConfig "github.com/lxc/lxd/lxd/device/config"
        firewallConsts "github.com/lxc/lxd/lxd/firewall/consts"
+       "github.com/lxc/lxd/lxd/instance"
        "github.com/lxc/lxd/lxd/instance/instancetype"
        "github.com/lxc/lxd/lxd/project"
        "github.com/lxc/lxd/shared"
@@ -40,8 +41,8 @@ type proxyProcInfo struct {
 }
 
 // validateConfig checks the supplied config for correctness.
-func (d *proxy) validateConfig() error {
-       if d.inst.Type() != instancetype.Container {
+func (d *proxy) validateConfig(instConf instance.ConfigReader) error {
+       if !instanceSupported(instConf.Type(), instancetype.Container) {
                return ErrUnsupportedDevType
        }
 
diff --git a/lxd/device/unix_common.go b/lxd/device/unix_common.go
index ba32170f90..87de76f724 100644
--- a/lxd/device/unix_common.go
+++ b/lxd/device/unix_common.go
@@ -6,6 +6,7 @@ import (
        "strings"
 
        deviceConfig "github.com/lxc/lxd/lxd/device/config"
+       "github.com/lxc/lxd/lxd/instance"
        "github.com/lxc/lxd/lxd/instance/instancetype"
        "github.com/lxc/lxd/shared"
 )
@@ -38,8 +39,8 @@ func (d *unixCommon) isRequired() bool {
 }
 
 // validateConfig checks the supplied config for correctness.
-func (d *unixCommon) validateConfig() error {
-       if d.inst.Type() != instancetype.Container {
+func (d *unixCommon) validateConfig(instConf instance.ConfigReader) error {
+       if !instanceSupported(instConf.Type(), instancetype.Container) {
                return ErrUnsupportedDevType
        }
 
diff --git a/lxd/device/unix_hotplug.go b/lxd/device/unix_hotplug.go
index 5e02f96176..628b580077 100644
--- a/lxd/device/unix_hotplug.go
+++ b/lxd/device/unix_hotplug.go
@@ -10,6 +10,7 @@ import (
        udev "github.com/farjump/go-libudev"
 
        deviceConfig "github.com/lxc/lxd/lxd/device/config"
+       "github.com/lxc/lxd/lxd/instance"
        "github.com/lxc/lxd/lxd/instance/instancetype"
        "github.com/lxc/lxd/shared"
 )
@@ -41,8 +42,8 @@ func (d *unixHotplug) isRequired() bool {
 }
 
 // validateConfig checks the supplied config for correctness.
-func (d *unixHotplug) validateConfig() error {
-       if d.inst.Type() != instancetype.Container {
+func (d *unixHotplug) validateConfig(instConf instance.ConfigReader) error {
+       if !instanceSupported(instConf.Type(), instancetype.Container) {
                return ErrUnsupportedDevType
        }
 
diff --git a/lxd/device/usb.go b/lxd/device/usb.go
index fd42b33f45..161f79a478 100644
--- a/lxd/device/usb.go
+++ b/lxd/device/usb.go
@@ -8,6 +8,7 @@ import (
        "strings"
 
        deviceConfig "github.com/lxc/lxd/lxd/device/config"
+       "github.com/lxc/lxd/lxd/instance"
        "github.com/lxc/lxd/lxd/instance/instancetype"
        "github.com/lxc/lxd/shared"
 )
@@ -42,8 +43,8 @@ func (d *usb) isRequired() bool {
 }
 
 // validateConfig checks the supplied config for correctness.
-func (d *usb) validateConfig() error {
-       if d.inst.Type() != instancetype.Container {
+func (d *usb) validateConfig(instConf instance.ConfigReader) error {
+       if !instanceSupported(instConf.Type(), instancetype.Container) {
                return ErrUnsupportedDevType
        }
 
_______________________________________________
lxc-devel mailing list
lxc-devel@lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to