[lxc-devel] [lxd/master] Instance: Common driver and profile device validation support

tomponline on Github lxc-bot at linuxcontainers.org
Thu Jan 30 13:57:56 UTC 2020


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 1611 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20200130/5dd0acb7/attachment-0001.bin>
-------------- next part --------------
From 8be13df905cbaed9f1eb57c88014e8f65313261a Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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
 	}
 


More information about the lxc-devel mailing list