[lxc-devel] [lxd/master] Network: Adds feature.networks project feature

tomponline on Github lxc-bot at linuxcontainers.org
Tue Aug 25 09:06:03 UTC 2020


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 677 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20200825/cab93d7f/attachment-0001.bin>
-------------- next part --------------
From 6f967e5037a0a6c36ffbfc2d41fad7ffba9e6596 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 19 Aug 2020 15:29:34 +0100
Subject: [PATCH 01/61] doc/projects: Adds features.networks

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 doc/projects.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/doc/projects.md b/doc/projects.md
index 60e3c37a62..7a597e8990 100644
--- a/doc/projects.md
+++ b/doc/projects.md
@@ -20,6 +20,7 @@ Key                                  | Type      | Condition             | Defau
 features.images                      | boolean   | -                     | true                      | Separate set of images and image aliases for the project
 features.profiles                    | boolean   | -                     | true                      | Separate set of profiles for the project
 features.storage.volumes             | boolean   | -                     | true                      | Separate set of storage volumes for the project
+features.networks                    | boolean   | -                     | true                      | Separate set of networks for the project
 limits.containers                    | integer   | -                     | -                         | Maximum number of containers that can be created in the project
 limits.virtual-machines              | integer   | -                     | -                         | Maximum number of VMs that can be created in the project
 limits.cpu                           | integer   | -                     | -                         | Maximum value for the sum of individual "limits.cpu" configs set on the instances of the project

From e63db1060a42374e168c667546fbee932a6ef49e Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 19 Aug 2020 15:12:32 +0100
Subject: [PATCH 02/61] lxd/project: Adds NetworkProject function

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/project/project.go | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/lxd/project/project.go b/lxd/project/project.go
index 3d923d0d95..7a00bd8516 100644
--- a/lxd/project/project.go
+++ b/lxd/project/project.go
@@ -98,3 +98,32 @@ func StorageVolumeProject(c *db.Cluster, projectName string, volumeType int) (st
 
 	return Default, nil
 }
+
+// NetworkProject returns the project name to use for the network based on the requested project.
+// If the project specified has the "features.networks" flag enabled then the project name is returned,
+// otherwise the default project name is returned.
+func NetworkProject(c *db.Cluster, projectName string) (string, error) {
+	var project *api.Project
+	var err error
+
+	err = c.Transaction(func(tx *db.ClusterTx) error {
+		project, err = tx.GetProject(projectName)
+		if err != nil {
+			return err
+		}
+
+		return nil
+	})
+
+	if err != nil {
+		return "", errors.Wrapf(err, "Failed to load project %q", projectName)
+	}
+
+	// Networks only use the project specified if the project has the features.networks feature enabled,
+	// otherwise the legacy behaviour of using the default project for networks is used.
+	if shared.IsTrue(project.Config["features.networks"]) {
+		return projectName, nil
+	}
+
+	return Default, nil
+}

From 76c01ea1d5ee2749baf1939633d194bd4599d1f9 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 19 Aug 2020 15:29:56 +0100
Subject: [PATCH 03/61] lxc/project: Adds features.networks to project list
 output

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxc/project.go | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/lxc/project.go b/lxc/project.go
index b6a0fc6c61..d5a360ce3a 100644
--- a/lxc/project.go
+++ b/lxc/project.go
@@ -441,13 +441,18 @@ func (c *cmdProjectList) Run(cmd *cobra.Command, args []string) error {
 			storageVolumes = i18n.G("YES")
 		}
 
+		networks := i18n.G("NO")
+		if shared.IsTrue(project.Config["features.networks"]) {
+			networks = i18n.G("YES")
+		}
+
 		name := project.Name
 		if name == currentProject {
 			name = fmt.Sprintf("%s (%s)", name, i18n.G("current"))
 		}
 
 		strUsedBy := fmt.Sprintf("%d", len(project.UsedBy))
-		data = append(data, []string{name, images, profiles, storageVolumes, strUsedBy})
+		data = append(data, []string{name, images, profiles, storageVolumes, networks, strUsedBy})
 	}
 	sort.Sort(byName(data))
 
@@ -456,6 +461,7 @@ func (c *cmdProjectList) Run(cmd *cobra.Command, args []string) error {
 		i18n.G("IMAGES"),
 		i18n.G("PROFILES"),
 		i18n.G("STORAGE VOLUMES"),
+		i18n.G("NETWORKS"),
 		i18n.G("USED BY"),
 	}
 

From 642b3d993d1835acf4e03f182e24948b7b1eb171 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:17:02 +0100
Subject: [PATCH 04/61] lxd/db/networks: Updates networkState and usage to
 support projects

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/db/networks.go | 30 +++++++++++++++---------------
 1 file changed, 15 insertions(+), 15 deletions(-)

diff --git a/lxd/db/networks.go b/lxd/db/networks.go
index c41c06fc3b..e23a7a56af 100644
--- a/lxd/db/networks.go
+++ b/lxd/db/networks.go
@@ -301,18 +301,18 @@ func (c *ClusterTx) CreatePendingNetwork(node string, projectName string, name s
 }
 
 // NetworkCreated sets the state of the given network to "Created".
-func (c *ClusterTx) NetworkCreated(name string) error {
-	return c.networkState(name, networkCreated)
+func (c *ClusterTx) NetworkCreated(project string, name string) error {
+	return c.networkState(project, name, networkCreated)
 }
 
 // NetworkErrored sets the state of the given network to "Errored".
-func (c *ClusterTx) NetworkErrored(name string) error {
-	return c.networkState(name, networkErrored)
+func (c *ClusterTx) NetworkErrored(project string, name string) error {
+	return c.networkState(project, name, networkErrored)
 }
 
-func (c *ClusterTx) networkState(name string, state int) error {
-	stmt := "UPDATE networks SET state=? WHERE name=?"
-	result, err := c.tx.Exec(stmt, state, name)
+func (c *ClusterTx) networkState(project string, name string, state int) error {
+	stmt := "UPDATE networks SET state=? WHERE project_id = (SELECT id FROM projects WHERE name = ?) AND name=?"
+	result, err := c.tx.Exec(stmt, state, project, name)
 	if err != nil {
 		return err
 	}
@@ -363,22 +363,22 @@ func (c *ClusterTx) networkNodes(networkID int64) ([]string, error) {
 }
 
 // GetNetworks returns the names of existing networks.
-func (c *Cluster) GetNetworks() ([]string, error) {
-	return c.networks("")
+func (c *Cluster) GetNetworks(project string) ([]string, error) {
+	return c.networks(project, "")
 }
 
 // GetNonPendingNetworks returns the names of all networks that are not pending.
-func (c *Cluster) GetNonPendingNetworks() ([]string, error) {
-	return c.networks("NOT state=?", networkPending)
+func (c *Cluster) GetNonPendingNetworks(project string) ([]string, error) {
+	return c.networks(project, "NOT state=?", networkPending)
 }
 
 // Get all networks matching the given WHERE filter (if given).
-func (c *Cluster) networks(where string, args ...interface{}) ([]string, error) {
-	q := "SELECT name FROM networks"
-	inargs := []interface{}{}
+func (c *Cluster) networks(project string, where string, args ...interface{}) ([]string, error) {
+	q := "SELECT name FROM networks WHERE project_id = (SELECT id FROM projects WHERE name = ?)"
+	inargs := []interface{}{project}
 
 	if where != "" {
-		q += fmt.Sprintf(" WHERE %s", where)
+		q += fmt.Sprintf(" AND %s", where)
 		for _, arg := range args {
 			inargs = append(inargs, arg)
 		}

From df22cb132593fd678e370e8b1badd6c9291aca63 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:17:25 +0100
Subject: [PATCH 05/61] lxd/db/networks: Updates getNetwork and usage to
 support projects

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/db/networks.go | 24 ++++++++++++------------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/lxd/db/networks.go b/lxd/db/networks.go
index e23a7a56af..d5e63919bf 100644
--- a/lxd/db/networks.go
+++ b/lxd/db/networks.go
@@ -420,20 +420,20 @@ const (
 // GetNetworkInAnyState returns the network with the given name.
 //
 // The network can be in any state.
-func (c *Cluster) GetNetworkInAnyState(name string) (int64, *api.Network, error) {
-	return c.getNetwork(name, false)
+func (c *Cluster) GetNetworkInAnyState(project string, name string) (int64, *api.Network, error) {
+	return c.getNetwork(project, name, false)
 }
 
 // Get the network with the given name. If onlyCreated is true, only return
 // networks in the created state.
-func (c *Cluster) getNetwork(name string, onlyCreated bool) (int64, *api.Network, error) {
+func (c *Cluster) getNetwork(project string, name string, onlyCreated bool) (int64, *api.Network, error) {
 	description := sql.NullString{}
 	id := int64(-1)
 	state := 0
 	var netType NetworkType
 
-	q := "SELECT id, description, state, type FROM networks WHERE name=?"
-	arg1 := []interface{}{name}
+	q := "SELECT id, description, state, type FROM networks WHERE project_id = (SELECT id FROM projects WHERE name = ?) AND name=?"
+	arg1 := []interface{}{project, name}
 	arg2 := []interface{}{&id, &description, &state, &netType}
 	if onlyCreated {
 		q += " AND state=?"
@@ -648,8 +648,8 @@ func (c *Cluster) CreateNetwork(projectName string, name string, description str
 }
 
 // UpdateNetwork updates the network with the given name.
-func (c *Cluster) UpdateNetwork(name, description string, config map[string]string) error {
-	id, netInfo, err := c.GetNetworkInAnyState(name)
+func (c *Cluster) UpdateNetwork(project string, name, description string, config map[string]string) error {
+	id, netInfo, err := c.GetNetworkInAnyState(project, name)
 	if err != nil {
 		return err
 	}
@@ -662,7 +662,7 @@ func (c *Cluster) UpdateNetwork(name, description string, config map[string]stri
 
 		// Update network status if change applied successfully.
 		if netInfo.Status == api.NetworkStatusErrored {
-			err = tx.NetworkCreated(name)
+			err = tx.NetworkCreated(project, name)
 			if err != nil {
 				return err
 			}
@@ -722,8 +722,8 @@ func clearNetworkConfig(tx *sql.Tx, networkID, nodeID int64) error {
 }
 
 // DeleteNetwork deletes the network with the given name.
-func (c *Cluster) DeleteNetwork(name string) error {
-	id, _, err := c.GetNetworkInAnyState(name)
+func (c *Cluster) DeleteNetwork(project string, name string) error {
+	id, _, err := c.GetNetworkInAnyState(project, name)
 	if err != nil {
 		return err
 	}
@@ -737,8 +737,8 @@ func (c *Cluster) DeleteNetwork(name string) error {
 }
 
 // RenameNetwork renames a network.
-func (c *Cluster) RenameNetwork(oldName string, newName string) error {
-	id, _, err := c.GetNetworkInAnyState(oldName)
+func (c *Cluster) RenameNetwork(project string, oldName string, newName string) error {
+	id, _, err := c.GetNetworkInAnyState(project, oldName)
 	if err != nil {
 		return err
 	}

From 7f34c3663d1de14cf4f011a63160a015d2d4ba7e Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:52:15 +0100
Subject: [PATCH 06/61] lxd/network/network/utils: Updates IsInUseByInstance to
 translate instance's project to a network project

Then matches the instance's network project against the requested network's project.

If they don't match then we consider this instance not in use by the requested network because its devices must be referencing a network in a different project.

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/network/network_utils.go | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)

diff --git a/lxd/network/network_utils.go b/lxd/network/network_utils.go
index b4757742d3..4605a8a73f 100644
--- a/lxd/network/network_utils.go
+++ b/lxd/network/network_utils.go
@@ -70,8 +70,20 @@ func networkValidPort(value string) error {
 
 // IsInUseByInstance indicates if network is referenced by an instance's NIC devices.
 // Checks if the device's parent or network properties match the network name.
-func IsInUseByInstance(s *state.State, c instance.Instance, networkName string) (bool, error) {
-	return isInUseByDevices(s, c.ExpandedDevices(), networkName)
+func IsInUseByInstance(s *state.State, inst instance.Instance, networkProjectName string, networkName string) (bool, error) {
+	// Get the translated network project name from the instance's project.
+	instNetworkProjectName, err := project.NetworkProject(s.Cluster, inst.Project())
+	if err != nil {
+		return false, err
+	}
+
+	// Skip instances who's translated network project doesn't match the requested network's project.
+	// Because its devices can't be using this network.
+	if networkProjectName != instNetworkProjectName {
+		return false, nil
+	}
+
+	return isInUseByDevices(s, networkProjectName, networkName, inst.ExpandedDevices())
 }
 
 // IsInUseByProfile indicates if network is referenced by a profile's NIC devices.

From b45e5a78ba390f0bd304dbf08d6fff2e823426e5 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:54:59 +0100
Subject: [PATCH 07/61] lxd/network/network/utils: Updates isInUseByDevices to
 support projects

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

diff --git a/lxd/network/network_utils.go b/lxd/network/network_utils.go
index 4605a8a73f..d7e2b723cd 100644
--- a/lxd/network/network_utils.go
+++ b/lxd/network/network_utils.go
@@ -92,13 +92,13 @@ func IsInUseByProfile(s *state.State, profile api.Profile, networkName string) (
 	return isInUseByDevices(s, deviceConfig.NewDevices(profile.Devices), networkName)
 }
 
-func isInUseByDevices(s *state.State, devices deviceConfig.Devices, networkName string) (bool, error) {
+func isInUseByDevices(s *state.State, networkProjectName string, networkName string, devices deviceConfig.Devices) (bool, error) {
 	for _, d := range devices {
 		if d["type"] != "nic" {
 			continue
 		}
 
-		nicType, err := nictype.NICType(s, d)
+		nicType, err := nictype.NICType(s, networkProjectName, d)
 		if err != nil {
 			return false, err
 		}

From 34deb941d451ed964884acfaa9a9dbd330ac5432 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:55:35 +0100
Subject: [PATCH 08/61] lxd/network/network/utils: Updates IsInUseByProfile to
 accept a db.Profile rather than api.Profile

This allows Profile's project to be used to be translated to a network project for comparison against the specified network's project.

If they don't match then the profile's devices are considered not in use by this network as the devices must be referencing a network in a different project.

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/network/network_utils.go | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)

diff --git a/lxd/network/network_utils.go b/lxd/network/network_utils.go
index d7e2b723cd..827ec20481 100644
--- a/lxd/network/network_utils.go
+++ b/lxd/network/network_utils.go
@@ -17,6 +17,7 @@ import (
 
 	"github.com/pkg/errors"
 
+	"github.com/lxc/lxd/lxd/db"
 	deviceConfig "github.com/lxc/lxd/lxd/device/config"
 	"github.com/lxc/lxd/lxd/device/nictype"
 	"github.com/lxc/lxd/lxd/dnsmasq"
@@ -88,8 +89,20 @@ func IsInUseByInstance(s *state.State, inst instance.Instance, networkProjectNam
 
 // IsInUseByProfile indicates if network is referenced by a profile's NIC devices.
 // Checks if the device's parent or network properties match the network name.
-func IsInUseByProfile(s *state.State, profile api.Profile, networkName string) (bool, error) {
-	return isInUseByDevices(s, deviceConfig.NewDevices(profile.Devices), networkName)
+func IsInUseByProfile(s *state.State, profile db.Profile, networkProjectName string, networkName string) (bool, error) {
+	// Get the translated network project name from the profiles's project.
+	profileNetworkProjectName, err := project.NetworkProject(s.Cluster, profile.Project)
+	if err != nil {
+		return false, err
+	}
+
+	// Skip profiles who's translated network project doesn't match the requested network's project.
+	// Because its devices can't be using this network.
+	if networkProjectName != profileNetworkProjectName {
+		return false, nil
+	}
+
+	return isInUseByDevices(s, networkProjectName, networkName, deviceConfig.NewDevices(profile.Devices))
 }
 
 func isInUseByDevices(s *state.State, networkProjectName string, networkName string, devices deviceConfig.Devices) (bool, error) {

From 813dd9c79494259a2b8d1311ab43c8e364264a03 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:57:24 +0100
Subject: [PATCH 09/61] lxd/network/network/utils: Updates UpdateDNSMasqStatic
 to use default project

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/network/network_utils.go | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/lxd/network/network_utils.go b/lxd/network/network_utils.go
index 827ec20481..fd618b4303 100644
--- a/lxd/network/network_utils.go
+++ b/lxd/network/network_utils.go
@@ -268,7 +268,9 @@ func UpdateDNSMasqStatic(s *state.State, networkName string) error {
 	var networks []string
 	if networkName == "" {
 		var err error
-		networks, err = s.Cluster.GetNetworks()
+
+		// Pass project.Default here, as currently dnsmasq (bridged) networks do not support projects.
+		networks, err = s.Cluster.GetNetworks(project.Default)
 		if err != nil {
 			return err
 		}
@@ -292,7 +294,7 @@ func UpdateDNSMasqStatic(s *state.State, networkName string) error {
 				continue
 			}
 
-			nicType, err := nictype.NICType(s, d)
+			nicType, err := nictype.NICType(s, inst.Project(), d)
 			if err != nil || nicType != "bridged" {
 				continue
 			}
@@ -347,7 +349,8 @@ func UpdateDNSMasqStatic(s *state.State, networkName string) error {
 			continue
 		}
 
-		n, err := LoadByName(s, network)
+		// Pass project.Default here, as currently dnsmasq (bridged) networks do not support projects.
+		n, err := LoadByName(s, project.Default, network)
 		if err != nil {
 			return err
 		}

From ac8ed5ceaf3893d9b5d959fe991c1c4a5bbe17d6 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:57:42 +0100
Subject: [PATCH 10/61] lxd/network/network/utils: Updates GetLeaseAddresses to
 use default project

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/network/network_utils.go | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/lxd/network/network_utils.go b/lxd/network/network_utils.go
index fd618b4303..50978f320e 100644
--- a/lxd/network/network_utils.go
+++ b/lxd/network/network_utils.go
@@ -729,7 +729,8 @@ func GetLeaseAddresses(s *state.State, networkName string, hwaddr string) ([]api
 		return addresses, nil
 	}
 
-	dbInfo, err := LoadByName(s, networkName)
+	// Pass project.Default here, as currently dnsmasq (bridged) networks do not support projects.
+	dbInfo, err := LoadByName(s, project.Default, networkName)
 	if err != nil {
 		return nil, err
 	}

From 870055db1d0b6fc5efcaba504bdda2b985aee69b Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:58:22 +0100
Subject: [PATCH 11/61] lxd/network/driver/common: Updates IsInUseByInstance
 and IsInUseByProfile to support projects

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

diff --git a/lxd/network/driver_common.go b/lxd/network/driver_common.go
index 296106b82d..f0d9165657 100644
--- a/lxd/network/driver_common.go
+++ b/lxd/network/driver_common.go
@@ -133,7 +133,7 @@ func (n *common) IsUsed() (bool, error) {
 	}
 
 	for _, inst := range insts {
-		inUse, err := IsInUseByInstance(n.state, inst, n.name)
+		inUse, err := IsInUseByInstance(n.state, inst, n.project, n.name)
 		if err != nil {
 			return false, err
 		}
@@ -158,7 +158,7 @@ func (n *common) IsUsed() (bool, error) {
 	}
 
 	for _, profile := range profiles {
-		inUse, err := IsInUseByProfile(n.state, *db.ProfileToAPI(&profile), n.name)
+		inUse, err := IsInUseByProfile(n.state, profile, n.project, n.name)
 		if err != nil {
 			return false, err
 		}

From c5b57a3fcd76dd8c043a252c61b00b8f4e34b09a Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 19 Aug 2020 14:45:57 +0100
Subject: [PATCH 12/61] lxd/network/driver/bridge: Adds existing interface
 check as Create function

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/network/driver_bridge.go | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/lxd/network/driver_bridge.go b/lxd/network/driver_bridge.go
index 562fe48280..739663c26b 100644
--- a/lxd/network/driver_bridge.go
+++ b/lxd/network/driver_bridge.go
@@ -378,6 +378,17 @@ func (n *bridge) Validate(config map[string]string) error {
 	return nil
 }
 
+// Create checks whether the bridge interface name is used already.
+func (n *bridge) Create(clusterNotification bool) error {
+	n.logger.Debug("Create", log.Ctx{"clusterNotification": clusterNotification, "config": n.config})
+
+	if shared.PathExists(fmt.Sprintf("/sys/class/net/%s", n.name)) {
+		return fmt.Errorf("Network interface %q already exists", n.name)
+	}
+
+	return nil
+}
+
 // isRunning returns whether the network is up.
 func (n *bridge) isRunning() bool {
 	return shared.PathExists(fmt.Sprintf("/sys/class/net/%s", n.name))

From e5a14fd02035b9cec9fbe03fe973bbba72076ae2 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 19 Aug 2020 14:46:35 +0100
Subject: [PATCH 13/61] lxd/network/driver/bridge: Push down interface name
 conflict check to Rename

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/network/driver_bridge.go | 12 +++---------
 1 file changed, 3 insertions(+), 9 deletions(-)

diff --git a/lxd/network/driver_bridge.go b/lxd/network/driver_bridge.go
index 739663c26b..24fd74d11d 100644
--- a/lxd/network/driver_bridge.go
+++ b/lxd/network/driver_bridge.go
@@ -419,14 +419,8 @@ func (n *bridge) Delete(clusterNotification bool) error {
 func (n *bridge) Rename(newName string) error {
 	n.logger.Debug("Rename", log.Ctx{"newName": newName})
 
-	// Sanity checks.
-	inUse, err := n.IsUsed()
-	if err != nil {
-		return err
-	}
-
-	if inUse {
-		return fmt.Errorf("The network is currently in use")
+	if shared.PathExists(fmt.Sprintf("/sys/class/net/%s", newName)) {
+		return fmt.Errorf("Network interface %q already exists", newName)
 	}
 
 	// Bring the network down.
@@ -447,7 +441,7 @@ func (n *bridge) Rename(newName string) error {
 	}
 
 	// Rename common steps.
-	err = n.common.rename(newName)
+	err := n.common.rename(newName)
 	if err != nil {
 		return err
 	}

From 678242b6eb1858c6f5c987267fd953f63d944eb0 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 19 Aug 2020 14:47:00 +0100
Subject: [PATCH 14/61] lxd/network/driver: Removes duplicated "in use" check
 that is now done at top level

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/network/driver_macvlan.go | 12 +-----------
 lxd/network/driver_ovn.go     | 12 +-----------
 lxd/network/driver_sriov.go   | 12 +-----------
 3 files changed, 3 insertions(+), 33 deletions(-)

diff --git a/lxd/network/driver_macvlan.go b/lxd/network/driver_macvlan.go
index 6195c6c9f0..8ef6786a8f 100644
--- a/lxd/network/driver_macvlan.go
+++ b/lxd/network/driver_macvlan.go
@@ -42,18 +42,8 @@ func (n *macvlan) Delete(clusterNotification bool) error {
 func (n *macvlan) Rename(newName string) error {
 	n.logger.Debug("Rename", log.Ctx{"newName": newName})
 
-	// Sanity checks.
-	inUse, err := n.IsUsed()
-	if err != nil {
-		return err
-	}
-
-	if inUse {
-		return fmt.Errorf("The network is currently in use")
-	}
-
 	// Rename common steps.
-	err = n.common.rename(newName)
+	err := n.common.rename(newName)
 	if err != nil {
 		return err
 	}
diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index 1c6d914674..b89c920e18 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -1121,18 +1121,8 @@ func (n *ovn) Delete(clusterNotification bool) error {
 func (n *ovn) Rename(newName string) error {
 	n.logger.Debug("Rename", log.Ctx{"newName": newName})
 
-	// Sanity checks.
-	inUse, err := n.IsUsed()
-	if err != nil {
-		return err
-	}
-
-	if inUse {
-		return fmt.Errorf("The network is currently in use")
-	}
-
 	// Rename common steps.
-	err = n.common.rename(newName)
+	err := n.common.rename(newName)
 	if err != nil {
 		return err
 	}
diff --git a/lxd/network/driver_sriov.go b/lxd/network/driver_sriov.go
index 2f3483ed75..91443edbb2 100644
--- a/lxd/network/driver_sriov.go
+++ b/lxd/network/driver_sriov.go
@@ -42,18 +42,8 @@ func (n *sriov) Delete(clusterNotification bool) error {
 func (n *sriov) Rename(newName string) error {
 	n.logger.Debug("Rename", log.Ctx{"newName": newName})
 
-	// Sanity checks.
-	inUse, err := n.IsUsed()
-	if err != nil {
-		return err
-	}
-
-	if inUse {
-		return fmt.Errorf("The network is currently in use")
-	}
-
 	// Rename common steps.
-	err = n.common.rename(newName)
+	err := n.common.rename(newName)
 	if err != nil {
 		return err
 	}

From 3e5ef8703027aa955738815ae795eb1195c6054b Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 19 Aug 2020 15:30:24 +0100
Subject: [PATCH 15/61] lxd/api/project: Adds features.networks support but
 does not enable by default

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/api_project.go | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/lxd/api_project.go b/lxd/api_project.go
index 25b43c95c1..874b61d54a 100644
--- a/lxd/api_project.go
+++ b/lxd/api_project.go
@@ -22,7 +22,13 @@ import (
 	"github.com/lxc/lxd/shared/version"
 )
 
-var projectFeatures = []string{"features.images", "features.profiles", "features.storage.volumes"}
+// projectFeatures are the features available to projects.
+var projectFeatures = []string{"features.images", "features.profiles", "features.storage.volumes", "features.networks"}
+
+// projectFeaturesDefaults are the features enabled by default on new projects.
+// The features.networks won't be enabled by default until it becomes clear whether it is practical to run OVN on
+// every system.
+var projectFeaturesDefaults = []string{"features.images", "features.profiles", "features.storage.volumes"}
 
 var projectsCmd = APIEndpoint{
 	Path: "projects",
@@ -100,7 +106,7 @@ func projectsPost(d *Daemon, r *http.Request) response.Response {
 	if project.Config == nil {
 		project.Config = map[string]string{}
 	}
-	for _, feature := range projectFeatures {
+	for _, feature := range projectFeaturesDefaults {
 		_, ok := project.Config[feature]
 		if !ok {
 			project.Config[feature] = "true"
@@ -526,6 +532,7 @@ var projectConfigKeys = map[string]func(value string) error{
 	"features.profiles":              validate.Optional(validate.IsBool),
 	"features.images":                validate.Optional(validate.IsBool),
 	"features.storage.volumes":       validate.Optional(validate.IsBool),
+	"features.networks":              validate.Optional(validate.IsBool),
 	"limits.containers":              validate.Optional(validate.IsUint32),
 	"limits.virtual-machines":        validate.Optional(validate.IsUint32),
 	"limits.memory":                  validate.Optional(validate.IsSize),

From a7ff127dbe2ddbf35141be304ce2ba94ab56c4ac Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 09:59:08 +0100
Subject: [PATCH 16/61] lxd/profiles/utils: Renames project arg to projectName
 in doProfileUpdate

And updates usage of ValidDevices() by passing projectName to it.

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/profiles_utils.go | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/lxd/profiles_utils.go b/lxd/profiles_utils.go
index 672d584d10..c82a7d3dda 100644
--- a/lxd/profiles_utils.go
+++ b/lxd/profiles_utils.go
@@ -13,10 +13,10 @@ import (
 	"github.com/pkg/errors"
 )
 
-func doProfileUpdate(d *Daemon, project, name string, id int64, profile *api.Profile, req api.ProfilePut) error {
+func doProfileUpdate(d *Daemon, projectName string, name string, id int64, profile *api.Profile, req api.ProfilePut) error {
 	// Check project limits.
 	err := d.cluster.Transaction(func(tx *db.ClusterTx) error {
-		return projecthelpers.AllowProfileUpdate(tx, project, name, req)
+		return projecthelpers.AllowProfileUpdate(tx, projectName, name, req)
 	})
 	if err != nil {
 		return err
@@ -29,12 +29,12 @@ func doProfileUpdate(d *Daemon, project, name string, id int64, profile *api.Pro
 	}
 
 	// 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)
+	err = instance.ValidDevices(d.State(), d.cluster, projectName, instancetype.Any, deviceConfig.NewDevices(req.Devices), false)
 	if err != nil {
 		return err
 	}
 
-	containers, err := getProfileContainersInfo(d.cluster, project, name)
+	containers, err := getProfileContainersInfo(d.cluster, projectName, name)
 	if err != nil {
 		return errors.Wrapf(err, "failed to query instances associated with profile '%s'", name)
 	}
@@ -77,8 +77,8 @@ func doProfileUpdate(d *Daemon, project, name string, id int64, profile *api.Pro
 
 	// Update the database
 	err = d.cluster.Transaction(func(tx *db.ClusterTx) error {
-		return tx.UpdateProfile(project, name, db.Profile{
-			Project:     project,
+		return tx.UpdateProfile(projectName, name, db.Profile{
+			Project:     projectName,
 			Name:        name,
 			Description: req.Description,
 			Config:      req.Config,

From 13c2ed0f6be9a6340f23d310105f42b940c43675 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:00:10 +0100
Subject: [PATCH 17/61] lxd/profiles: Updates usage of ValidDevices in
 profilesPost

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

diff --git a/lxd/profiles.go b/lxd/profiles.go
index 7783ecb5d2..2cba1eefa7 100644
--- a/lxd/profiles.go
+++ b/lxd/profiles.go
@@ -111,7 +111,7 @@ func profilesPost(d *Daemon, r *http.Request) response.Response {
 	}
 
 	// 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)
+	err = instance.ValidDevices(d.State(), d.cluster, projectName, instancetype.Any, deviceConfig.NewDevices(req.Devices), false)
 	if err != nil {
 		return response.BadRequest(err)
 	}

From 2f083e417e9a46baf45393fe99067ac3df108af7 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:00:58 +0100
Subject: [PATCH 18/61] lxd/patches: Updates to support network projects

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/patches.go | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)

diff --git a/lxd/patches.go b/lxd/patches.go
index 65d61031a1..a5d849721f 100644
--- a/lxd/patches.go
+++ b/lxd/patches.go
@@ -298,7 +298,8 @@ func patchInvalidProfileNames(name string, d *Daemon) error {
 
 func patchNetworkPermissions(name string, d *Daemon) error {
 	// Get the list of networks
-	networks, err := d.cluster.GetNetworks()
+	// Pass project.Default, as networks didn't support projects here.
+	networks, err := d.cluster.GetNetworks(project.Default)
 	if err != nil {
 		return err
 	}
@@ -2660,7 +2661,8 @@ func patchStorageZFSVolumeSize(name string, d *Daemon) error {
 
 func patchNetworkDnsmasqHosts(name string, d *Daemon) error {
 	// Get the list of networks
-	networks, err := d.cluster.GetNetworks()
+	// Pass project.Default, as dnsmasq (bridge) networks don't support projects.
+	networks, err := d.cluster.GetNetworks(project.Default)
 	if err != nil {
 		return err
 	}
@@ -3447,21 +3449,24 @@ func patchClusteringDropDatabaseRole(name string, d *Daemon) error {
 
 // patchNetworkCearBridgeVolatileHwaddr removes the unsupported `volatile.bridge.hwaddr` config key from networks.
 func patchNetworkCearBridgeVolatileHwaddr(name string, d *Daemon) error {
+	// Use project.Default, as bridge networks don't support projects.
+	projectName := project.Default
+
 	// Get the list of networks.
-	networks, err := d.cluster.GetNetworks()
+	networks, err := d.cluster.GetNetworks(projectName)
 	if err != nil {
 		return errors.Wrapf(err, "Failed loading networks for network_clear_bridge_volatile_hwaddr patch")
 	}
 
 	for _, networkName := range networks {
-		_, net, err := d.cluster.GetNetworkInAnyState(networkName)
+		_, net, err := d.cluster.GetNetworkInAnyState(projectName, networkName)
 		if err != nil {
 			return errors.Wrapf(err, "Failed loading network %q for network_clear_bridge_volatile_hwaddr patch", networkName)
 		}
 
 		if net.Config["volatile.bridge.hwaddr"] != "" {
 			delete(net.Config, "volatile.bridge.hwaddr")
-			err = d.cluster.UpdateNetwork(net.Name, net.Description, net.Config)
+			err = d.cluster.UpdateNetwork(projectName, net.Name, net.Description, net.Config)
 			if err != nil {
 				return errors.Wrapf(err, "Failed updating network %q for network_clear_bridge_volatile_hwaddr patch", networkName)
 			}

From a02da31ed17891e0c899c9f06ab8012aa625fba5 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:01:23 +0100
Subject: [PATCH 19/61] lxd/networks/utils: Removes networkGetInterfaces
 function

Now we have virtual networks we need to differentiate between interface names and network names.

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/networks_utils.go | 26 --------------------------
 1 file changed, 26 deletions(-)

diff --git a/lxd/networks_utils.go b/lxd/networks_utils.go
index ecda29c599..b568b66521 100644
--- a/lxd/networks_utils.go
+++ b/lxd/networks_utils.go
@@ -41,32 +41,6 @@ func networkAutoAttach(cluster *db.Cluster, devName string) error {
 	return network.AttachInterface(dbInfo.Name, devName)
 }
 
-func networkGetInterfaces(cluster *db.Cluster) ([]string, error) {
-	networks, err := cluster.GetNetworks()
-	if err != nil {
-		return nil, err
-	}
-
-	ifaces, err := net.Interfaces()
-	if err != nil {
-		return nil, err
-	}
-
-	for _, iface := range ifaces {
-		// Ignore veth pairs (for performance reasons)
-		if strings.HasPrefix(iface.Name, "veth") {
-			continue
-		}
-
-		// Append to the list
-		if !shared.StringInSlice(iface.Name, networks) {
-			networks = append(networks, iface.Name)
-		}
-	}
-
-	return networks, nil
-}
-
 // networkUpdateForkdnsServersTask runs every 30s and refreshes the forkdns servers list.
 func networkUpdateForkdnsServersTask(s *state.State, heartbeatData *cluster.APIHeartbeat) error {
 	// Get a list of managed networks

From 9718bb0d09e9dc322fa662d53df5c948b31d2ef8 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:02:19 +0100
Subject: [PATCH 20/61] lxd/networks/utils: Updates
 networkUpdateForkdnsServersTask to support projects

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/networks_utils.go | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/lxd/networks_utils.go b/lxd/networks_utils.go
index b568b66521..dc8e834353 100644
--- a/lxd/networks_utils.go
+++ b/lxd/networks_utils.go
@@ -11,6 +11,7 @@ import (
 	"github.com/lxc/lxd/lxd/cluster"
 	"github.com/lxc/lxd/lxd/db"
 	"github.com/lxc/lxd/lxd/network"
+	"github.com/lxc/lxd/lxd/project"
 	"github.com/lxc/lxd/lxd/state"
 	"github.com/lxc/lxd/shared"
 	"github.com/lxc/lxd/shared/api"
@@ -43,16 +44,19 @@ func networkAutoAttach(cluster *db.Cluster, devName string) error {
 
 // networkUpdateForkdnsServersTask runs every 30s and refreshes the forkdns servers list.
 func networkUpdateForkdnsServersTask(s *state.State, heartbeatData *cluster.APIHeartbeat) error {
+	// Use project.Default here as forkdns (fan bridge) networks don't support projects.
+	projectName := project.Default
+
 	// Get a list of managed networks
-	networks, err := s.Cluster.GetNonPendingNetworks()
+	networks, err := s.Cluster.GetNonPendingNetworks(projectName)
 	if err != nil {
 		return err
 	}
 
 	for _, name := range networks {
-		n, err := network.LoadByName(s, name)
+		n, err := network.LoadByName(s, projectName, name)
 		if err != nil {
-			logger.Errorf("Failed to load network %q for heartbeat", name)
+			logger.Errorf("Failed to load network %q from project %q for heartbeat", name, projectName)
 			continue
 		}
 

From fb68d471d0bdb914884b2f6ea8735c543f9956b8 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 19 Aug 2020 14:43:24 +0100
Subject: [PATCH 21/61] lxd/networks: Updates networkPost validation

 - Moves "in use" check up from drivers, to reduce duplicate in per-driver Rename() functions.
 - Relaxes new name checks to only check for existing managed networks of the same name, removes checks for conflicting interface names (this only needs to be applied to bridge type networks and will be pushed down into bridge driver).
 - General clean up of comments and error messages.

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/networks.go | 32 +++++++++++++++++++++++---------
 1 file changed, 23 insertions(+), 9 deletions(-)

diff --git a/lxd/networks.go b/lxd/networks.go
index 60bd4a8642..c3e2644773 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -570,28 +570,32 @@ func networkPost(d *Daemon, r *http.Request) response.Response {
 		return response.SmartError(err)
 	}
 	if clustered {
-		return response.BadRequest(fmt.Errorf("Renaming a network not supported in LXD clusters"))
+		return response.BadRequest(fmt.Errorf("Renaming clustered network not supported"))
 	}
 
 	name := mux.Vars(r)["name"]
 	req := api.NetworkPost{}
 	state := d.State()
 
-	// Parse the request
+	// Parse the request.
 	err = json.NewDecoder(r.Body).Decode(&req)
 	if err != nil {
 		return response.BadRequest(err)
 	}
 
-	// Get the existing network
+	// Get the existing network.
 	n, err := network.LoadByName(state, name)
 	if err != nil {
-		return response.NotFound(err)
+		if err == db.ErrNoSuchObject {
+			return response.NotFound(fmt.Errorf("Network not found"))
+		}
+
+		return response.InternalError(errors.Wrapf(err, "Failed loading network"))
 	}
 
-	// Sanity checks
+	// Sanity check new name.
 	if req.Name == "" {
-		return response.BadRequest(fmt.Errorf("No name provided"))
+		return response.BadRequest(fmt.Errorf("New network name not provided"))
 	}
 
 	err = network.ValidateName(req.Name, n.Type())
@@ -599,8 +603,18 @@ func networkPost(d *Daemon, r *http.Request) response.Response {
 		return response.BadRequest(err)
 	}
 
-	// Check that the name isn't already in use
-	networks, err := networkGetInterfaces(d.cluster)
+	// Check network isn't in use.
+	inUse, err := n.IsUsed()
+	if err != nil {
+		return response.InternalError(errors.Wrapf(err, "Failed checking network in use"))
+	}
+
+	if inUse {
+		return response.BadRequest(fmt.Errorf("Network is currently in use"))
+	}
+
+	// Check that the name isn't already in used by an existing managed network.
+	networks, err := d.cluster.GetNetworks()
 	if err != nil {
 		return response.InternalError(err)
 	}
@@ -609,7 +623,7 @@ func networkPost(d *Daemon, r *http.Request) response.Response {
 		return response.Conflict(fmt.Errorf("Network %q already exists", req.Name))
 	}
 
-	// Rename it
+	// Rename it.
 	err = n.Rename(req.Name)
 	if err != nil {
 		return response.SmartError(err)

From 7fc20816cfae1b3291a7993b992d67e32fc44ee2 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:04:07 +0100
Subject: [PATCH 22/61] lxd/networks: Updates networksGet to support projects

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/networks.go | 34 ++++++++++++++++++++++++++++++----
 1 file changed, 30 insertions(+), 4 deletions(-)

diff --git a/lxd/networks.go b/lxd/networks.go
index c3e2644773..49c4f56576 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -66,20 +66,46 @@ var networkStateCmd = APIEndpoint{
 
 // API endpoints
 func networksGet(d *Daemon, r *http.Request) response.Response {
+	projectName, err := project.NetworkProject(d.State().Cluster, projectParam(r))
+	if err != nil {
+		return response.SmartError(err)
+	}
+
 	recursion := util.IsRecursionRequest(r)
 
-	ifs, err := networkGetInterfaces(d.cluster)
+	// Get list of managed networks (that may or may not have network interfaces on the host).
+	networks, err := d.cluster.GetNetworks(projectName)
 	if err != nil {
 		return response.InternalError(err)
 	}
 
+	// Get list of actual network interfaces on the host as well if the network's project is Default.
+	if projectName == project.Default {
+		ifaces, err := net.Interfaces()
+		if err != nil {
+			return response.InternalError(err)
+		}
+
+		for _, iface := range ifaces {
+			// Ignore veth pairs (for performance reasons).
+			if strings.HasPrefix(iface.Name, "veth") {
+				continue
+			}
+
+			// Append to the list of networks if a managed network of same name doesn't exist.
+			if !shared.StringInSlice(iface.Name, networks) {
+				networks = append(networks, iface.Name)
+			}
+		}
+	}
+
 	resultString := []string{}
 	resultMap := []api.Network{}
-	for _, iface := range ifs {
+	for _, network := range networks {
 		if !recursion {
-			resultString = append(resultString, fmt.Sprintf("/%s/networks/%s", version.APIVersion, iface))
+			resultString = append(resultString, fmt.Sprintf("/%s/networks/%s", version.APIVersion, network))
 		} else {
-			net, err := doNetworkGet(d, iface)
+			net, err := doNetworkGet(d, projectName, network)
 			if err != nil {
 				continue
 			}

From 9f8ff0b4e0ddc1342591e0f9b8b0ad3b243f8a93 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 19 Aug 2020 15:13:21 +0100
Subject: [PATCH 23/61] lxd/networks: Updates networksPost to support projects

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/networks.go | 21 +++++++++++++--------
 1 file changed, 13 insertions(+), 8 deletions(-)

diff --git a/lxd/networks.go b/lxd/networks.go
index 49c4f56576..43d9cd212b 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -121,13 +121,18 @@ func networksGet(d *Daemon, r *http.Request) response.Response {
 }
 
 func networksPost(d *Daemon, r *http.Request) response.Response {
+	projectName, err := project.NetworkProject(d.State().Cluster, projectParam(r))
+	if err != nil {
+		return response.SmartError(err)
+	}
+
 	networkCreateLock.Lock()
 	defer networkCreateLock.Unlock()
 
 	req := api.NetworksPost{}
 
 	// Parse the request.
-	err := json.NewDecoder(r.Body).Decode(&req)
+	err = json.NewDecoder(r.Body).Decode(&req)
 	if err != nil {
 		return response.BadRequest(err)
 	}
@@ -171,7 +176,7 @@ func networksPost(d *Daemon, r *http.Request) response.Response {
 	if isClusterNotification(r) {
 		// This is an internal request which triggers the actual creation of the network across all nodes
 		// after they have been previously defined.
-		err = doNetworksCreate(d, req, true)
+		err = doNetworksCreate(d, projectName, req, true)
 		if err != nil {
 			return response.SmartError(err)
 		}
@@ -189,7 +194,7 @@ func networksPost(d *Daemon, r *http.Request) response.Response {
 		}
 
 		err = d.cluster.Transaction(func(tx *db.ClusterTx) error {
-			return tx.CreatePendingNetwork(targetNode, project.Default, req.Name, dbNetType, req.Config)
+			return tx.CreatePendingNetwork(targetNode, projectName, req.Name, dbNetType, req.Config)
 		})
 		if err != nil {
 			if err == db.ErrAlreadyDefined {
@@ -207,7 +212,7 @@ func networksPost(d *Daemon, r *http.Request) response.Response {
 	}
 
 	if count > 1 {
-		err = networksPostCluster(d, req)
+		err = networksPostCluster(d, projectName, req)
 		if err != nil {
 			return response.SmartError(err)
 		}
@@ -221,7 +226,7 @@ func networksPost(d *Daemon, r *http.Request) response.Response {
 		return response.SmartError(err)
 	}
 
-	networks, err := networkGetInterfaces(d.cluster)
+	networks, err := d.cluster.GetNetworks(projectName)
 	if err != nil {
 		return response.InternalError(err)
 	}
@@ -234,17 +239,17 @@ func networksPost(d *Daemon, r *http.Request) response.Response {
 	defer revert.Fail()
 
 	// Create the database entry.
-	_, err = d.cluster.CreateNetwork(project.Default, req.Name, req.Description, dbNetType, req.Config)
+	_, err = d.cluster.CreateNetwork(projectName, req.Name, req.Description, dbNetType, req.Config)
 	if err != nil {
 		return response.SmartError(errors.Wrapf(err, "Error inserting %q into database", req.Name))
 	}
 
 	revert.Add(func() {
-		d.cluster.DeleteNetwork(req.Name)
+		d.cluster.DeleteNetwork(projectName, req.Name)
 	})
 
 	// Create network and pass false to clusterNotification so the database record is removed on error.
-	err = doNetworksCreate(d, req, false)
+	err = doNetworksCreate(d, projectName, req, false)
 	if err != nil {
 		return response.SmartError(err)
 	}

From 5d5dbf81135419d007d2e5b412090a613135124b Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:05:09 +0100
Subject: [PATCH 24/61] lxd/networks: Updates networksPostCluster to support
 projects

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/networks.go | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/lxd/networks.go b/lxd/networks.go
index 43d9cd212b..77b48103cb 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -258,7 +258,7 @@ func networksPost(d *Daemon, r *http.Request) response.Response {
 	return resp
 }
 
-func networksPostCluster(d *Daemon, req api.NetworksPost) error {
+func networksPostCluster(d *Daemon, projectName string, req api.NetworksPost) error {
 	// Check that no node-specific config key has been defined.
 	for key := range req.Config {
 		if shared.StringInSlice(key, db.NodeSpecificNetworkConfig) {
@@ -268,7 +268,7 @@ func networksPostCluster(d *Daemon, req api.NetworksPost) error {
 
 	// Check that the requested network type matches the type created when adding the local node config.
 	// If network doesn't exist yet, ignore not found error, as this will be checked by NetworkNodeConfigs().
-	_, netInfo, err := d.cluster.GetNetworkInAnyState(req.Name)
+	_, netInfo, err := d.cluster.GetNetworkInAnyState(projectName, req.Name)
 	if err != nil && err != db.ErrNoSuchObject {
 		return err
 	}
@@ -334,20 +334,20 @@ func networksPostCluster(d *Daemon, req api.NetworksPost) error {
 
 	revert.Add(func() {
 		d.cluster.Transaction(func(tx *db.ClusterTx) error {
-			return tx.NetworkErrored(req.Name)
+			return tx.NetworkErrored(projectName, req.Name)
 		})
 	})
 
 	// We need to mark the network as created now, because the network.LoadByName call invoked by
 	// doNetworksCreate would fail with not-found otherwise.
 	err = d.cluster.Transaction(func(tx *db.ClusterTx) error {
-		return tx.NetworkCreated(req.Name)
+		return tx.NetworkCreated(projectName, req.Name)
 	})
 	if err != nil {
 		return err
 	}
 
-	err = doNetworksCreate(d, nodeReq, false)
+	err = doNetworksCreate(d, projectName, nodeReq, false)
 	if err != nil {
 		return err
 	}

From a0a7d7aff71eeb456c3028bd889e92d61b4119f5 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:05:32 +0100
Subject: [PATCH 25/61] lxd/networks: Updates doNetworksCreate to support
 projects

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

diff --git a/lxd/networks.go b/lxd/networks.go
index 77b48103cb..5fbd5b0d50 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -375,9 +375,9 @@ func networksPostCluster(d *Daemon, projectName string, req api.NetworksPost) er
 
 // Create the network on the system. The clusterNotification flag is used to indicate whether creation request
 // is coming from a cluster notification (and if so we should not delete the database record on error).
-func doNetworksCreate(d *Daemon, req api.NetworksPost, clusterNotification bool) error {
+func doNetworksCreate(d *Daemon, projectName string, req api.NetworksPost, clusterNotification bool) error {
 	// Start the network.
-	n, err := network.LoadByName(d.State(), req.Name)
+	n, err := network.LoadByName(d.State(), projectName, req.Name)
 	if err != nil {
 		return err
 	}

From 2370acc07f36e2042d9f4c1b3d01471adec3b610 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:06:32 +0100
Subject: [PATCH 26/61] lxd/networks: Updates networkGet to support projects

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/networks.go | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/lxd/networks.go b/lxd/networks.go
index 5fbd5b0d50..505920ff64 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -410,9 +410,14 @@ func networkGet(d *Daemon, r *http.Request) response.Response {
 		return resp
 	}
 
+	projectName, err := project.NetworkProject(d.State().Cluster, projectParam(r))
+	if err != nil {
+		return response.SmartError(err)
+	}
+
 	name := mux.Vars(r)["name"]
 
-	n, err := doNetworkGet(d, name)
+	n, err := doNetworkGet(d, projectName, name)
 	if err != nil {
 		return response.SmartError(err)
 	}

From 670a97f7716214d177db4b4e2e7734c96437f001 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:07:00 +0100
Subject: [PATCH 27/61] lxd/networks: Updates doNetworkGet to support projects

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/networks.go | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/lxd/networks.go b/lxd/networks.go
index 505920ff64..77d26e32c2 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -441,28 +441,28 @@ func networkGet(d *Daemon, r *http.Request) response.Response {
 	return response.SyncResponseETag(true, &n, etag)
 }
 
-func doNetworkGet(d *Daemon, name string) (api.Network, error) {
-	// Ignore veth pairs (for performance reasons)
+func doNetworkGet(d *Daemon, projectName string, name string) (api.Network, error) {
+	// Ignore veth pairs (for performance reasons).
 	if strings.HasPrefix(name, "veth") {
 		return api.Network{}, os.ErrNotExist
 	}
 
-	// Get some information
+	// Get some information.
 	osInfo, _ := net.InterfaceByName(name)
-	_, dbInfo, _ := d.cluster.GetNetworkInAnyState(name)
+	_, dbInfo, _ := d.cluster.GetNetworkInAnyState(projectName, name)
 
-	// Sanity check
+	// Sanity check.
 	if osInfo == nil && dbInfo == nil {
 		return api.Network{}, os.ErrNotExist
 	}
 
-	// Prepare the response
+	// Prepare the response.
 	n := api.Network{}
 	n.Name = name
 	n.UsedBy = []string{}
 	n.Config = map[string]string{}
 
-	// Set the device type as needed
+	// Set the device type as needed.
 	if osInfo != nil && shared.IsLoopback(osInfo) {
 		n.Type = "loopback"
 	} else if dbInfo != nil {
@@ -487,7 +487,7 @@ func doNetworkGet(d *Daemon, name string) (api.Network, error) {
 		}
 	}
 
-	// Look for containers using the interface
+	// Look for instances using the interface.
 	if n.Type != "loopback" {
 		// Look at instances.
 		insts, err := instance.LoadFromAllProjects(d.State())
@@ -496,7 +496,7 @@ func doNetworkGet(d *Daemon, name string) (api.Network, error) {
 		}
 
 		for _, inst := range insts {
-			inUse, err := network.IsInUseByInstance(d.State(), inst, n.Name)
+			inUse, err := network.IsInUseByInstance(d.State(), inst, projectName, n.Name)
 			if err != nil {
 				return api.Network{}, err
 			}
@@ -525,7 +525,7 @@ func doNetworkGet(d *Daemon, name string) (api.Network, error) {
 		}
 
 		for _, profile := range profiles {
-			inUse, err := network.IsInUseByProfile(d.State(), *db.ProfileToAPI(&profile), n.Name)
+			inUse, err := network.IsInUseByProfile(d.State(), profile, projectName, n.Name)
 			if err != nil {
 				return api.Network{}, err
 			}

From 2929fd96f550ac149f1b00bc6a6171ed422a0324 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:07:22 +0100
Subject: [PATCH 28/61] lxd/networks: Updates networkDelete to support projects

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/networks.go | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/lxd/networks.go b/lxd/networks.go
index 77d26e32c2..e73ece0b44 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -549,16 +549,21 @@ func doNetworkGet(d *Daemon, projectName string, name string) (api.Network, erro
 }
 
 func networkDelete(d *Daemon, r *http.Request) response.Response {
+	projectName, err := project.NetworkProject(d.State().Cluster, projectParam(r))
+	if err != nil {
+		return response.SmartError(err)
+	}
+
 	name := mux.Vars(r)["name"]
 	state := d.State()
 
 	// Check if the network is pending, if so we just need to delete it from the database.
-	_, dbNetwork, err := d.cluster.GetNetworkInAnyState(name)
+	_, dbNetwork, err := d.cluster.GetNetworkInAnyState(projectName, name)
 	if err != nil {
 		return response.SmartError(err)
 	}
 	if dbNetwork.Status == api.NetworkStatusPending {
-		err := d.cluster.DeleteNetwork(name)
+		err := d.cluster.DeleteNetwork(projectName, name)
 		if err != nil {
 			return response.SmartError(err)
 		}
@@ -566,7 +571,7 @@ func networkDelete(d *Daemon, r *http.Request) response.Response {
 	}
 
 	// Get the existing network.
-	n, err := network.LoadByName(state, name)
+	n, err := network.LoadByName(state, projectName, name)
 	if err != nil {
 		return response.NotFound(err)
 	}

From 72beac2107b2f9dbd45b4290bc6148f73de1769b Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:07:41 +0100
Subject: [PATCH 29/61] lxd/networks: Updates networkPost to support projects

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/networks.go | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/lxd/networks.go b/lxd/networks.go
index e73ece0b44..0f3ba7fdbc 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -624,8 +624,13 @@ func networkPost(d *Daemon, r *http.Request) response.Response {
 		return response.BadRequest(err)
 	}
 
+	projectName, err := project.NetworkProject(d.State().Cluster, projectParam(r))
+	if err != nil {
+		return response.SmartError(err)
+	}
+
 	// Get the existing network.
-	n, err := network.LoadByName(state, name)
+	n, err := network.LoadByName(state, projectName, name)
 	if err != nil {
 		if err == db.ErrNoSuchObject {
 			return response.NotFound(fmt.Errorf("Network not found"))
@@ -655,7 +660,7 @@ func networkPost(d *Daemon, r *http.Request) response.Response {
 	}
 
 	// Check that the name isn't already in used by an existing managed network.
-	networks, err := d.cluster.GetNetworks()
+	networks, err := d.cluster.GetNetworks(projectName)
 	if err != nil {
 		return response.InternalError(err)
 	}

From a0b36f636090c0a944c63ebc3222b3b3c38fd99b Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:07:57 +0100
Subject: [PATCH 30/61] lxc/networks: Updates networkPut to support projects

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/networks.go | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/lxd/networks.go b/lxd/networks.go
index 0f3ba7fdbc..71234ed29d 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -685,10 +685,15 @@ func networkPut(d *Daemon, r *http.Request) response.Response {
 		return resp
 	}
 
+	projectName, err := project.NetworkProject(d.State().Cluster, projectParam(r))
+	if err != nil {
+		return response.SmartError(err)
+	}
+
 	name := mux.Vars(r)["name"]
 
 	// Get the existing network.
-	_, dbInfo, err := d.cluster.GetNetworkInAnyState(name)
+	_, dbInfo, err := d.cluster.GetNetworkInAnyState(projectName, name)
 	if err != nil {
 		return response.SmartError(err)
 	}
@@ -741,7 +746,7 @@ func networkPut(d *Daemon, r *http.Request) response.Response {
 		}
 	}
 
-	return doNetworkUpdate(d, name, req, targetNode, isClusterNotification(r), r.Method, clustered)
+	return doNetworkUpdate(d, projectName, name, req, targetNode, isClusterNotification(r), r.Method, clustered)
 }
 
 func networkPatch(d *Daemon, r *http.Request) response.Response {

From 9199075a0a44263a4a84094d94398bbd360f246c Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:08:10 +0100
Subject: [PATCH 31/61] lxd/networks: Updates doNetworkUpdate to support
 projects

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

diff --git a/lxd/networks.go b/lxd/networks.go
index 71234ed29d..e54a8d60b2 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -755,9 +755,9 @@ func networkPatch(d *Daemon, r *http.Request) response.Response {
 
 // doNetworkUpdate loads the current local network config, merges with the requested network config, validates
 // and applies the changes. Will also notify other cluster nodes of non-node specific config if needed.
-func doNetworkUpdate(d *Daemon, name string, req api.NetworkPut, targetNode string, clusterNotification bool, httpMethod string, clustered bool) response.Response {
+func doNetworkUpdate(d *Daemon, projectName string, name string, req api.NetworkPut, targetNode string, clusterNotification bool, httpMethod string, clustered bool) response.Response {
 	// Load the local node-specific network.
-	n, err := network.LoadByName(d.State(), name)
+	n, err := network.LoadByName(d.State(), projectName, name)
 	if err != nil {
 		return response.NotFound(err)
 	}

From aac8a687ba605e50b5111331e2b1b4b6fe104ffb Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:13:25 +0100
Subject: [PATCH 32/61] lxd/networks: Updates networkLeasesGet to support
 network projects

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/networks.go | 24 ++++++++++++++++--------
 1 file changed, 16 insertions(+), 8 deletions(-)

diff --git a/lxd/networks.go b/lxd/networks.go
index e54a8d60b2..c1e1302ac3 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -804,16 +804,24 @@ func doNetworkUpdate(d *Daemon, projectName string, name string, req api.Network
 }
 
 func networkLeasesGet(d *Daemon, r *http.Request) response.Response {
+	// The project we are filtering the instance leases by.
+	instProjectName := projectParam(r)
+
+	// The project we should use the load the network.
+	networkProjectName, err := project.NetworkProject(d.State().Cluster, instProjectName)
+	if err != nil {
+		return response.SmartError(err)
+	}
+
 	name := mux.Vars(r)["name"]
-	project := projectParam(r)
 
-	// Try to get the network
-	n, err := doNetworkGet(d, name)
+	// Try to get the network.
+	n, err := doNetworkGet(d, networkProjectName, name)
 	if err != nil {
 		return response.SmartError(err)
 	}
 
-	// Validate that we do have leases for it
+	// Validate that we do have leases for it.
 	if !n.Managed || n.Type != "bridge" {
 		return response.NotFound(errors.New("Leases not found"))
 	}
@@ -821,10 +829,10 @@ func networkLeasesGet(d *Daemon, r *http.Request) response.Response {
 	leases := []api.NetworkLease{}
 	projectMacs := []string{}
 
-	// Get all static leases
+	// Get all static leases.
 	if !isClusterNotification(r) {
-		// Get all the instances
-		instances, err := instance.LoadByProject(d.State(), project)
+		// Get all the instances.
+		instances, err := instance.LoadByProject(d.State(), instProjectName)
 		if err != nil {
 			return response.SmartError(err)
 		}
@@ -837,7 +845,7 @@ func networkLeasesGet(d *Daemon, r *http.Request) response.Response {
 					continue
 				}
 
-				nicType, err := nictype.NICType(d.State(), dev)
+				nicType, err := nictype.NICType(d.State(), inst.Project(), dev)
 				if err != nil || nicType != "bridged" {
 					continue
 				}

From fd4c0f8825b7e318b325914db34736e9d79ce4c8 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:13:44 +0100
Subject: [PATCH 33/61] lxd/networks: Updates networkStartup and
 networkShutdown to load networks from all projects

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/networks.go | 86 +++++++++++++++++++++++++++++++++----------------
 1 file changed, 58 insertions(+), 28 deletions(-)

diff --git a/lxd/networks.go b/lxd/networks.go
index c1e1302ac3..c10dd541db 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -986,31 +986,46 @@ func networkLeasesGet(d *Daemon, r *http.Request) response.Response {
 }
 
 func networkStartup(s *state.State) error {
-	// Get a list of managed networks.
-	networks, err := s.Cluster.GetNonPendingNetworks()
+	var err error
+
+	// Get a list of projects.
+	var projectNames []string
+
+	err = s.Cluster.Transaction(func(tx *db.ClusterTx) error {
+		projectNames, err = tx.GetProjectNames()
+		return err
+	})
 	if err != nil {
-		return errors.Wrapf(err, "Failed to load networks")
+		return errors.Wrapf(err, "Failed to load projects")
 	}
 
-	// Bring them all up.
-	for _, name := range networks {
-		n, err := network.LoadByName(s, name)
+	for _, projectName := range projectNames {
+		// Get a list of managed networks.
+		networks, err := s.Cluster.GetNonPendingNetworks(projectName)
 		if err != nil {
-			return errors.Wrapf(err, "Failed to load network %q", name)
+			return errors.Wrapf(err, "Failed to load networks for project %q", projectName)
 		}
 
-		err = n.Validate(n.Config())
-		if err != nil {
-			// Don't cause LXD to fail to start entirely on network start up failure.
-			logger.Error("Failed to validate network", log.Ctx{"err": err, "name": name})
-			continue
-		}
+		// Bring them all up.
+		for _, name := range networks {
+			n, err := network.LoadByName(s, projectName, name)
+			if err != nil {
+				return errors.Wrapf(err, "Failed to load network %q in project %q", name, projectName)
+			}
 
-		err = n.Start()
-		if err != nil {
-			// Don't cause LXD to fail to start entirely on network start up failure.
-			logger.Error("Failed to bring up network", log.Ctx{"err": err, "name": name})
-			continue
+			err = n.Validate(n.Config())
+			if err != nil {
+				// Don't cause LXD to fail to start entirely on network start up failure.
+				logger.Error("Failed to validate network", log.Ctx{"err": err, "project": projectName, "name": name})
+				continue
+			}
+
+			err = n.Start()
+			if err != nil {
+				// Don't cause LXD to fail to start entirely on network start up failure.
+				logger.Error("Failed to bring up network", log.Ctx{"err": err, "project": projectName, "name": name})
+				continue
+			}
 		}
 	}
 
@@ -1018,22 +1033,37 @@ func networkStartup(s *state.State) error {
 }
 
 func networkShutdown(s *state.State) error {
-	// Get a list of managed networks
-	networks, err := s.Cluster.GetNetworks()
-	if err != nil {
+	var err error
+
+	// Get a list of projects.
+	var projectNames []string
+
+	err = s.Cluster.Transaction(func(tx *db.ClusterTx) error {
+		projectNames, err = tx.GetProjectNames()
 		return err
+	})
+	if err != nil {
+		return errors.Wrapf(err, "Failed to load projects")
 	}
 
-	// Bring them all up
-	for _, name := range networks {
-		n, err := network.LoadByName(s, name)
+	for _, projectName := range projectNames {
+		// Get a list of managed networks.
+		networks, err := s.Cluster.GetNetworks(projectName)
 		if err != nil {
-			return err
+			return errors.Wrapf(err, "Failed to load networks for project %q", projectName)
 		}
 
-		err = n.Stop()
-		if err != nil {
-			logger.Error("Failed to bring down network", log.Ctx{"err": err, "name": name})
+		// Bring them all down.
+		for _, name := range networks {
+			n, err := network.LoadByName(s, projectName, name)
+			if err != nil {
+				return errors.Wrapf(err, "Failed to load network %q in project %q", name, projectName)
+			}
+
+			err = n.Stop()
+			if err != nil {
+				logger.Error("Failed to bring down network", log.Ctx{"err": err, "project": projectName, "name": name})
+			}
 		}
 	}
 

From 9089b7e0d7a626d7a3890530fae98f5687902f05 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:16:12 +0100
Subject: [PATCH 34/61] lxd/network/network/load: Updates load functions to
 support projects

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/network/network_load.go | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/lxd/network/network_load.go b/lxd/network/network_load.go
index 1757da08a2..dcd4419c3c 100644
--- a/lxd/network/network_load.go
+++ b/lxd/network/network_load.go
@@ -3,6 +3,7 @@ package network
 import (
 	"github.com/pkg/errors"
 
+	"github.com/lxc/lxd/lxd/project"
 	"github.com/lxc/lxd/lxd/state"
 	"github.com/lxc/lxd/shared/api"
 )
@@ -15,8 +16,8 @@ var drivers = map[string]func() Network{
 }
 
 // LoadByName loads the network info from the database by name.
-func LoadByName(s *state.State, name string) (Network, error) {
-	id, netInfo, err := s.Cluster.GetNetworkInAnyState(name)
+func LoadByName(s *state.State, project string, name string) (Network, error) {
+	id, netInfo, err := s.Cluster.GetNetworkInAnyState(project, name)
 	if err != nil {
 		return nil, err
 	}
@@ -27,7 +28,7 @@ func LoadByName(s *state.State, name string) (Network, error) {
 	}
 
 	n := driverFunc()
-	n.init(s, id, name, netInfo.Type, netInfo.Description, netInfo.Config, netInfo.Status)
+	n.init(s, id, project, name, netInfo.Type, netInfo.Description, netInfo.Config, netInfo.Status)
 
 	return n, nil
 }
@@ -40,7 +41,7 @@ func ValidateName(name string, netType string) error {
 	}
 
 	n := driverFunc()
-	n.init(nil, 0, name, netType, "", nil, "Unknown")
+	n.init(nil, 0, project.Default, name, netType, "", nil, "Unknown")
 
 	err := n.ValidateName(name)
 	if err != nil {
@@ -58,7 +59,7 @@ func Validate(name string, netType string, config map[string]string) error {
 	}
 
 	n := driverFunc()
-	n.init(nil, 0, name, netType, "", config, "Unknown")
+	n.init(nil, 0, project.Default, name, netType, "", config, "Unknown")
 
 	err := n.ValidateName(name)
 	if err != nil {
@@ -76,7 +77,7 @@ func FillConfig(req *api.NetworksPost) error {
 	}
 
 	n := driverFunc()
-	n.init(nil, 0, req.Name, req.Type, req.Description, req.Config, "Unknown")
+	n.init(nil, 0, project.Default, req.Name, req.Type, req.Description, req.Config, "Unknown")
 
 	err := n.fillConfig(req.Config)
 	if err != nil {

From 32bbb6d465c7f7018d1372d48d26197e2c0fc4d7 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:19:47 +0100
Subject: [PATCH 35/61] lxd/network/network/interface: Adds project name to
 init function

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/network/network_interface.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lxd/network/network_interface.go b/lxd/network/network_interface.go
index dfb9e3b470..84b038b779 100644
--- a/lxd/network/network_interface.go
+++ b/lxd/network/network_interface.go
@@ -12,7 +12,7 @@ import (
 // Network represents a LXD network.
 type Network interface {
 	// Load.
-	init(state *state.State, id int64, name string, netType string, description string, config map[string]string, status string)
+	init(state *state.State, id int64, projectName string, name string, netType string, description string, config map[string]string, status string)
 	fillConfig(config map[string]string) error
 
 	// Config.

From 42188c79f42f39dc425b83651b8b8413209eb7eb Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:21:29 +0100
Subject: [PATCH 36/61] lxd/network/driver/common: Adds project support

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/network/driver_common.go | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/lxd/network/driver_common.go b/lxd/network/driver_common.go
index f0d9165657..3fc229f56b 100644
--- a/lxd/network/driver_common.go
+++ b/lxd/network/driver_common.go
@@ -26,6 +26,7 @@ type common struct {
 	logger      logger.Logger
 	state       *state.State
 	id          int64
+	project     string
 	name        string
 	netType     string
 	description string
@@ -34,9 +35,10 @@ type common struct {
 }
 
 // init initialise internal variables.
-func (n *common) init(state *state.State, id int64, name string, netType string, description string, config map[string]string, status string) {
-	n.logger = logging.AddContext(logger.Log, log.Ctx{"driver": netType, "network": name})
+func (n *common) init(state *state.State, id int64, projectName string, name string, netType string, description string, config map[string]string, status string) {
+	n.logger = logging.AddContext(logger.Log, log.Ctx{"project": projectName, "driver": netType, "network": name})
 	n.id = id
+	n.project = projectName
 	n.name = name
 	n.netType = netType
 	n.config = config
@@ -225,7 +227,7 @@ func (n *common) DHCPv6Ranges() []shared.IPRange {
 func (n *common) update(applyNetwork api.NetworkPut, targetNode string, clusterNotification bool) error {
 	// Update internal config before database has been updated (so that if update is a notification we apply
 	// the config being supplied and not that in the database).
-	n.init(n.state, n.id, n.name, n.netType, applyNetwork.Description, applyNetwork.Config, n.status)
+	n.init(n.state, n.id, n.project, n.name, n.netType, applyNetwork.Description, applyNetwork.Config, n.status)
 
 	// If this update isn't coming via a cluster notification itself, then notify all nodes of change and then
 	// update the database.
@@ -257,7 +259,7 @@ func (n *common) update(applyNetwork api.NetworkPut, targetNode string, clusterN
 		}
 
 		// Update the database.
-		err := n.state.Cluster.UpdateNetwork(n.name, applyNetwork.Description, applyNetwork.Config)
+		err := n.state.Cluster.UpdateNetwork(n.project, n.name, applyNetwork.Description, applyNetwork.Config)
 		if err != nil {
 			return err
 		}
@@ -330,13 +332,13 @@ func (n *common) rename(newName string) error {
 	}
 
 	// Rename the database entry.
-	err := n.state.Cluster.RenameNetwork(n.name, newName)
+	err := n.state.Cluster.RenameNetwork(n.project, n.name, newName)
 	if err != nil {
 		return err
 	}
 
 	// Reinitialise internal name variable and logger context with new name.
-	n.init(n.state, n.id, newName, n.netType, n.description, n.config, n.status)
+	n.init(n.state, n.id, n.project, newName, n.netType, n.description, n.config, n.status)
 
 	return nil
 }
@@ -358,7 +360,7 @@ func (n *common) delete(clusterNotification bool) error {
 		}
 
 		// Remove the network from the database.
-		err = n.state.Cluster.DeleteNetwork(n.name)
+		err = n.state.Cluster.DeleteNetwork(n.project, n.name)
 		if err != nil {
 			return err
 		}

From 2f55fc2f495912412bb8b346a2b1ae91e19e290e Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:22:42 +0100
Subject: [PATCH 37/61] lxd/network/driver/ovn: Load parent network from
 default project

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/network/driver_ovn.go | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index b89c920e18..f422404754 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -263,7 +263,8 @@ func (n *ovn) getIntSwitchInstancePortPrefix() string {
 // setupParentPort initialises the parent uplink connection. Returns the derived ovnParentVars settings used
 // during the initial creation of the logical network.
 func (n *ovn) setupParentPort(routerMAC net.HardwareAddr) (*ovnParentVars, error) {
-	parentNet, err := LoadByName(n.state, n.config["parent"])
+	// Parent network must be in default project.
+	parentNet, err := LoadByName(n.state, project.Default, n.config["parent"])
 	if err != nil {
 		return nil, errors.Wrapf(err, "Failed loading parent network")
 	}
@@ -486,7 +487,8 @@ func (n *ovn) parentAllocateIP(ipRanges []*shared.IPRange, allAllocated []net.IP
 
 // startParentPort performs any network start up logic needed to connect the parent uplink connection to OVN.
 func (n *ovn) startParentPort() error {
-	parentNet, err := LoadByName(n.state, n.config["parent"])
+	// Parent network must be in default project.
+	parentNet, err := LoadByName(n.state, project.Default, n.config["parent"])
 	if err != nil {
 		return errors.Wrapf(err, "Failed loading parent network")
 	}
@@ -612,7 +614,8 @@ func (n *ovn) startParentPortBridge(parentNet Network) error {
 
 // deleteParentPort deletes the parent uplink connection.
 func (n *ovn) deleteParentPort() error {
-	parentNet, err := LoadByName(n.state, n.config["parent"])
+	// Parent network must be in default project.
+	parentNet, err := LoadByName(n.state, project.Default, n.config["parent"])
 	if err != nil {
 		return errors.Wrapf(err, "Failed loading parent network")
 	}

From 7564744b938f63acec2cfa59977392d5838eed37 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:46:43 +0100
Subject: [PATCH 38/61] lxd/device/nictype: Adds conversion of device project
 to network project for NICType validation

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/device/nictype/nictype.go | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/lxd/device/nictype/nictype.go b/lxd/device/nictype/nictype.go
index a72e8f1ccd..7d83fc042c 100644
--- a/lxd/device/nictype/nictype.go
+++ b/lxd/device/nictype/nictype.go
@@ -8,6 +8,7 @@ import (
 	"github.com/pkg/errors"
 
 	deviceConfig "github.com/lxc/lxd/lxd/device/config"
+	"github.com/lxc/lxd/lxd/project"
 	"github.com/lxc/lxd/lxd/state"
 )
 
@@ -15,13 +16,19 @@ import (
 // If the device "type" is "nic" and the "network" property is specified in the device config, then NIC type is
 // resolved from the network's type. Otherwise the device's "nictype" property is returned (which may be empty if
 // used with non-NIC device configs).
-func NICType(s *state.State, d deviceConfig.Device) (string, error) {
+func NICType(s *state.State, deviceProjectName string, d deviceConfig.Device) (string, error) {
 	// NIC devices support resolving their "nictype" from their "network" property.
 	if d["type"] == "nic" {
 		if d["network"] != "" {
-			_, netInfo, err := s.Cluster.GetNetworkInAnyState(d["network"])
+			// Translate device's project name into a network project name.
+			networkProjectName, err := project.NetworkProject(s.Cluster, deviceProjectName)
 			if err != nil {
-				return "", errors.Wrapf(err, "Failed to load network %q", d["network"])
+				return "", errors.Wrapf(err, "Failed to translate device project %q into network project", deviceProjectName)
+			}
+
+			_, netInfo, err := s.Cluster.GetNetworkInAnyState(networkProjectName, d["network"])
+			if err != nil {
+				return "", errors.Wrapf(err, "Failed to load network %q for project %q", d["network"], networkProjectName)
 			}
 
 			var nicType string

From 62a965e6cd96c295c75645c7eaec216ab3ea4dca Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:23:12 +0100
Subject: [PATCH 39/61] lxd/instance/instance/interface: Moves Project()
 function into ConfigReader interface

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/instance/instance_interface.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lxd/instance/instance_interface.go b/lxd/instance/instance_interface.go
index e03db25bfb..f0c117f6e7 100644
--- a/lxd/instance/instance_interface.go
+++ b/lxd/instance/instance_interface.go
@@ -33,6 +33,7 @@ const (
 
 // ConfigReader is used to read instance config.
 type ConfigReader interface {
+	Project() string
 	Type() instancetype.Type
 	ExpandedConfig() map[string]string
 	ExpandedDevices() deviceConfig.Devices
@@ -99,7 +100,6 @@ type Instance interface {
 	// Properties.
 	ID() int
 	Location() string
-	Project() string
 	Name() string
 	Description() string
 	Architecture() int

From ce5846d480b373ce1cae65c9866b468f8fd4cbfb Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:23:41 +0100
Subject: [PATCH 40/61] lxd/instance/instance/utils: Project name is needed to
 validate instance devices

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/instance/instance_utils.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lxd/instance/instance_utils.go b/lxd/instance/instance_utils.go
index 5dd419c8b6..7af24f7ee8 100644
--- a/lxd/instance/instance_utils.go
+++ b/lxd/instance/instance_utils.go
@@ -31,7 +31,7 @@ import (
 )
 
 // ValidDevices is linked from instance/drivers.validDevices to validate device config.
-var ValidDevices func(state *state.State, cluster *db.Cluster, instanceType instancetype.Type, devices deviceConfig.Devices, expanded bool) error
+var ValidDevices func(state *state.State, cluster *db.Cluster, projectName string, 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 e30ea9ed5ca23da87fc3ec3163b341378c9328c8 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:24:15 +0100
Subject: [PATCH 41/61] lxd/instance: instance.ValidDevices project argument
 usage

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

diff --git a/lxd/instance.go b/lxd/instance.go
index 9b63bb49a0..e865de0068 100644
--- a/lxd/instance.go
+++ b/lxd/instance.go
@@ -476,7 +476,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.Devices, false)
+	err = instance.ValidDevices(s, s.Cluster, args.Project, args.Type, args.Devices, false)
 	if err != nil {
 		return nil, errors.Wrap(err, "Invalid devices")
 	}

From d2e950d359ef8c07e0b6512e23d06797c7ac25e8 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:24:48 +0100
Subject: [PATCH 42/61] lxd/instance/drivers/driver/lxc: Updates lxc to use
 common fields

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/instance/drivers/driver_lxc.go | 40 ++++++++++++++----------------
 1 file changed, 19 insertions(+), 21 deletions(-)

diff --git a/lxd/instance/drivers/driver_lxc.go b/lxd/instance/drivers/driver_lxc.go
index bc4669a06b..768b536b8b 100644
--- a/lxd/instance/drivers/driver_lxc.go
+++ b/lxd/instance/drivers/driver_lxc.go
@@ -143,22 +143,24 @@ func lxcStatusCode(state liblxc.State) api.StatusCode {
 func lxcCreate(s *state.State, args db.InstanceArgs) (instance.Instance, error) {
 	// Create the container struct
 	c := &lxc{
-		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,
 		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,
 	}
 
@@ -399,20 +401,22 @@ func lxcUnload(c *lxc) {
 // Create a container struct without initializing it.
 func lxcInstantiate(s *state.State, args db.InstanceArgs, expandedDevices deviceConfig.Devices) instance.Instance {
 	c := &lxc{
-		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,
@@ -441,32 +445,26 @@ func lxcInstantiate(s *state.State, args db.InstanceArgs, expandedDevices device
 
 // The LXC container driver.
 type lxc 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
-	fromHook        bool
-	localConfig     map[string]string
-	localDevices    deviceConfig.Devices
-	profiles        []string
+	fromHook bool
 
 	// Cache
 	c       *liblxc.Container
 	cConfig bool
 
-	state    *state.State
 	idmapset *idmap.IdmapSet
 
 	// Storage

From dfda5c252e14174e7ca716e32d365943634dedad Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:25:07 +0100
Subject: [PATCH 43/61] lxd/instance/drivers/driver/lxc: instance.ValidDevices
 project usage

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/instance/drivers/driver_lxc.go | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/lxd/instance/drivers/driver_lxc.go b/lxd/instance/drivers/driver_lxc.go
index 768b536b8b..5ffd9237af 100644
--- a/lxd/instance/drivers/driver_lxc.go
+++ b/lxd/instance/drivers/driver_lxc.go
@@ -201,7 +201,7 @@ func lxcCreate(s *state.State, args db.InstanceArgs) (instance.Instance, error)
 		return nil, err
 	}
 
-	err = instance.ValidDevices(s, s.Cluster, c.Type(), c.expandedDevices, true)
+	err = instance.ValidDevices(s, s.Cluster, c.Project(), c.Type(), c.expandedDevices, true)
 	if err != nil {
 		c.Delete()
 		logger.Error("Failed creating container", ctxMap)
@@ -3841,7 +3841,7 @@ func (c *lxc) 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(), args.Devices, false)
+		err = instance.ValidDevices(c.state, c.state.Cluster, c.Project(), c.Type(), args.Devices, false)
 		if err != nil {
 			return errors.Wrap(err, "Invalid devices")
 		}
@@ -4022,7 +4022,7 @@ func (c *lxc) 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.expandedDevices, true)
+		err = instance.ValidDevices(c.state, c.state.Cluster, c.Project(), c.Type(), c.expandedDevices, true)
 		if err != nil {
 			return errors.Wrap(err, "Invalid expanded devices")
 		}

From 071ae9ca55df1c8034dcd0d355e24fee04ae3dee Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:25:23 +0100
Subject: [PATCH 44/61] lxd/instance/drivers/driver/lxc: Error quoting

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/instance/drivers/driver_lxc.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lxd/instance/drivers/driver_lxc.go b/lxd/instance/drivers/driver_lxc.go
index 5ffd9237af..3df620de27 100644
--- a/lxd/instance/drivers/driver_lxc.go
+++ b/lxd/instance/drivers/driver_lxc.go
@@ -348,7 +348,7 @@ func lxcCreate(s *state.State, args db.InstanceArgs) (instance.Instance, error)
 			err = c.deviceAdd(k, m)
 			if err != nil && err != device.ErrUnsupportedDevType {
 				c.Delete()
-				return nil, errors.Wrapf(err, "Failed to add device '%s'", k)
+				return nil, errors.Wrapf(err, "Failed to add device %q", k)
 			}
 		}
 

From 5f67576e394236db0c59ed7e8c3a146da2c94c32 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:26:05 +0100
Subject: [PATCH 45/61] lxc/instance/drivers/driver/lxc: nictype.NICType
 project usage

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/instance/drivers/driver_lxc.go | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/lxd/instance/drivers/driver_lxc.go b/lxd/instance/drivers/driver_lxc.go
index 3df620de27..778caeaa10 100644
--- a/lxd/instance/drivers/driver_lxc.go
+++ b/lxd/instance/drivers/driver_lxc.go
@@ -1780,12 +1780,12 @@ func (c *lxc) deviceResetVolatile(devName string, oldConfig, newConfig deviceCon
 	volatileClear := make(map[string]string)
 	devicePrefix := fmt.Sprintf("volatile.%s.", devName)
 
-	newNICType, err := nictype.NICType(c.state, newConfig)
+	newNICType, err := nictype.NICType(c.state, c.Project(), newConfig)
 	if err != nil {
 		return err
 	}
 
-	oldNICType, err := nictype.NICType(c.state, oldConfig)
+	oldNICType, err := nictype.NICType(c.state, c.Project(), oldConfig)
 	if err != nil {
 		return err
 	}
@@ -3991,12 +3991,12 @@ func (c *lxc) Update(args db.InstanceArgs, userRequested bool) error {
 		// devices are otherwise identical except for the fields returned here, then the
 		// device is considered to be being "updated" rather than "added & removed".
 
-		oldNICType, err := nictype.NICType(c.state, newDevice)
+		oldNICType, err := nictype.NICType(c.state, c.Project(), newDevice)
 		if err != nil {
 			return []string{} // Cannot hot-update due to config error.
 		}
 
-		newNICType, err := nictype.NICType(c.state, oldDevice)
+		newNICType, err := nictype.NICType(c.state, c.Project(), oldDevice)
 		if err != nil {
 			return []string{} // Cannot hot-update due to config error.
 		}
@@ -6402,7 +6402,7 @@ func (c *lxc) FillNetworkDevice(name string, m deviceConfig.Device) (deviceConfi
 		return nil
 	}
 
-	nicType, err := nictype.NICType(c.state, m)
+	nicType, err := nictype.NICType(c.state, c.Project(), m)
 	if err != nil {
 		return nil, err
 	}

From 356e75c8c8e5020fa7f6ddae5f00e4f882af2c9e Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:27:11 +0100
Subject: [PATCH 46/61] lxd/instance/drivers/driver/lxc: Removes driver
 specific Project function

Not needed as in common.

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/instance/drivers/driver_lxc.go | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/lxd/instance/drivers/driver_lxc.go b/lxd/instance/drivers/driver_lxc.go
index 778caeaa10..82c1ab5f1b 100644
--- a/lxd/instance/drivers/driver_lxc.go
+++ b/lxd/instance/drivers/driver_lxc.go
@@ -6727,11 +6727,6 @@ func (c *lxc) Location() string {
 	return c.node
 }
 
-// Project returns instance project.
-func (c *lxc) Project() string {
-	return c.project
-}
-
 // Name returns instance name.
 func (c *lxc) Name() string {
 	return c.name

From b50d0657ef415208ead238bf3e0e9a1f1b617bf8 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:28:16 +0100
Subject: [PATCH 47/61] lxd/instance/drivers/driver/qemu: instance.ValidDevices
 project usage

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/instance/drivers/driver_qemu.go | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/lxd/instance/drivers/driver_qemu.go b/lxd/instance/drivers/driver_qemu.go
index 90cb7045f5..85cffa4fe5 100644
--- a/lxd/instance/drivers/driver_qemu.go
+++ b/lxd/instance/drivers/driver_qemu.go
@@ -219,7 +219,7 @@ func qemuCreate(s *state.State, args db.InstanceArgs) (instance.Instance, error)
 		return nil, err
 	}
 
-	err = instance.ValidDevices(s, s.Cluster, vm.Type(), vm.expandedDevices, true)
+	err = instance.ValidDevices(s, s.Cluster, vm.Project(), vm.Type(), vm.expandedDevices, true)
 	if err != nil {
 		logger.Error("Failed creating instance", ctxMap)
 		return nil, errors.Wrap(err, "Invalid devices")
@@ -2773,7 +2773,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(), args.Devices, false)
+		err = instance.ValidDevices(vm.state, vm.state.Cluster, vm.Project(), vm.Type(), args.Devices, false)
 		if err != nil {
 			return errors.Wrap(err, "Invalid devices")
 		}
@@ -2946,7 +2946,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.expandedDevices, true)
+		err = instance.ValidDevices(vm.state, vm.state.Cluster, vm.Project(), vm.Type(), vm.expandedDevices, true)
 		if err != nil {
 			return errors.Wrap(err, "Invalid expanded devices")
 		}

From 3cb06a6a41229086ee4a6b2d5a721cd022b87630 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:28:33 +0100
Subject: [PATCH 48/61] lxd/instance/drivers/driver/qemu: nictype.NICType
 project usage

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/instance/drivers/driver_qemu.go | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/lxd/instance/drivers/driver_qemu.go b/lxd/instance/drivers/driver_qemu.go
index 85cffa4fe5..1ff5b459e2 100644
--- a/lxd/instance/drivers/driver_qemu.go
+++ b/lxd/instance/drivers/driver_qemu.go
@@ -2915,12 +2915,12 @@ func (vm *qemu) Update(args db.InstanceArgs, userRequested bool) error {
 		// between oldDevice and newDevice. The result of this is that as long as the
 		// devices are otherwise identical except for the fields returned here, then the
 		// device is considered to be being "updated" rather than "added & removed".
-		oldNICType, err := nictype.NICType(vm.state, newDevice)
+		oldNICType, err := nictype.NICType(vm.state, vm.Project(), newDevice)
 		if err != nil {
 			return []string{} // Cannot hot-update due to config error.
 		}
 
-		newNICType, err := nictype.NICType(vm.state, oldDevice)
+		newNICType, err := nictype.NICType(vm.state, vm.Project(), oldDevice)
 		if err != nil {
 			return []string{} // Cannot hot-update due to config error.
 		}
@@ -3105,12 +3105,12 @@ func (vm *qemu) deviceResetVolatile(devName string, oldConfig, newConfig deviceC
 	volatileClear := make(map[string]string)
 	devicePrefix := fmt.Sprintf("volatile.%s.", devName)
 
-	newNICType, err := nictype.NICType(vm.state, newConfig)
+	newNICType, err := nictype.NICType(vm.state, vm.Project(), newConfig)
 	if err != nil {
 		return err
 	}
 
-	oldNICType, err := nictype.NICType(vm.state, oldConfig)
+	oldNICType, err := nictype.NICType(vm.state, vm.Project(), oldConfig)
 	if err != nil {
 		return err
 	}
@@ -4075,7 +4075,7 @@ func (vm *qemu) RenderState() (*api.InstanceState, error) {
 			status.Processes = -1
 			networks := map[string]api.InstanceStateNetwork{}
 			for k, m := range vm.ExpandedDevices() {
-				nicType, err := nictype.NICType(vm.state, m)
+				nicType, err := nictype.NICType(vm.state, vm.Project(), m)
 				if err != nil {
 					return nil, err
 				}
@@ -4473,7 +4473,7 @@ func (vm *qemu) FillNetworkDevice(name string, m deviceConfig.Device) (deviceCon
 		return nil
 	}
 
-	nicType, err := nictype.NICType(vm.state, m)
+	nicType, err := nictype.NICType(vm.state, vm.Project(), m)
 	if err != nil {
 		return nil, err
 	}

From d3703038f7001e19fc50621e898e3544670b27a4 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:28:46 +0100
Subject: [PATCH 49/61] lxd/instance/drivers/driver/qemu: Removes driver
 specific Project function

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/instance/drivers/driver_qemu.go | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/lxd/instance/drivers/driver_qemu.go b/lxd/instance/drivers/driver_qemu.go
index 1ff5b459e2..7f0377a8b7 100644
--- a/lxd/instance/drivers/driver_qemu.go
+++ b/lxd/instance/drivers/driver_qemu.go
@@ -4282,11 +4282,6 @@ func (vm *qemu) Location() string {
 	return vm.node
 }
 
-// Project returns instance's project.
-func (vm *qemu) Project() string {
-	return vm.project
-}
-
 // Name returns the instance's name.
 func (vm *qemu) Name() string {
 	return vm.name

From 32bc8d9553b9826a9ae30f33bcbb6023f526a317 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:29:28 +0100
Subject: [PATCH 50/61] lxd/instance/drivers/driver/common: Adds Project
 function

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/instance/drivers/driver_common.go | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/lxd/instance/drivers/driver_common.go b/lxd/instance/drivers/driver_common.go
index cceb455fc9..27d0fd1c81 100644
--- a/lxd/instance/drivers/driver_common.go
+++ b/lxd/instance/drivers/driver_common.go
@@ -20,6 +20,11 @@ type common struct {
 	state           *state.State
 }
 
+// Project returns instance's project.
+func (c *common) Project() string {
+	return c.project
+}
+
 // Type returns the instance's type.
 func (c *common) Type() instancetype.Type {
 	return c.dbType

From bee8698fadec2c13a50344387bcc8ced7a897afb Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:30:00 +0100
Subject: [PATCH 51/61] lxd/instance/drivers/load: Adds project support to
 validDevices

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/instance/drivers/load.go | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/lxd/instance/drivers/load.go b/lxd/instance/drivers/load.go
index b534365fd0..f478c75ed4 100644
--- a/lxd/instance/drivers/load.go
+++ b/lxd/instance/drivers/load.go
@@ -47,7 +47,7 @@ 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, devices deviceConfig.Devices, expanded bool) error {
+func validDevices(state *state.State, cluster *db.Cluster, projectName string, instanceType instancetype.Type, devices deviceConfig.Devices, expanded bool) error {
 	// Empty device list
 	if devices == nil {
 		return nil
@@ -56,6 +56,7 @@ func validDevices(state *state.State, cluster *db.Cluster, instanceType instance
 	instConf := &common{
 		dbType:       instanceType,
 		localDevices: devices.Clone(),
+		project:      projectName,
 	}
 
 	// In non-expanded validation expensive checks should be avoided.
@@ -70,7 +71,7 @@ func validDevices(state *state.State, cluster *db.Cluster, instanceType instance
 	for name, config := range instConf.localDevices {
 		err := device.Validate(instConf, state, name, config)
 		if err != nil {
-			return errors.Wrapf(err, "Device validation failed %q", name)
+			return errors.Wrapf(err, "Device validation failed for %q", name)
 		}
 
 	}

From 4ea5a2bed42cc93813cdbc77bce609a1e28ab1ca Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:43:02 +0100
Subject: [PATCH 52/61] lxd/device/device/load: Adds project support to load
 function

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/device/device_load.go | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/lxd/device/device_load.go b/lxd/device/device_load.go
index feee9a0bfb..c89161224c 100644
--- a/lxd/device/device_load.go
+++ b/lxd/device/device_load.go
@@ -10,13 +10,15 @@ import (
 )
 
 // 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) {
+func load(inst instance.Instance, state *state.State, projectName string, name string, conf deviceConfig.Device, volatileGet VolatileGetter, volatileSet VolatileSetter) (device, error) {
+	// Warning: When validating a profile, inst is expected to be provided as nil.
+
 	if conf["type"] == "" {
 		return nil, fmt.Errorf("Missing device type for device %q", name)
 	}
 
 	// NIC type is required to lookup network devices.
-	nicType, err := nictype.NICType(state, conf)
+	nicType, err := nictype.NICType(state, projectName, conf)
 	if err != nil {
 		return nil, err
 	}
@@ -82,7 +84,7 @@ func load(inst instance.Instance, state *state.State, name string, conf deviceCo
 // 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)
+	dev, err := load(inst, state, inst.Project(), name, conf, volatileGet, volatileSet)
 	if err != nil {
 		return nil, err
 	}
@@ -98,7 +100,7 @@ func New(inst instance.Instance, state *state.State, name string, conf deviceCon
 // 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)
+	dev, err := load(nil, state, instConfig.Project(), name, conf, nil, nil)
 	if err != nil {
 		return err
 	}

From 009cfa3da1e0f5bcf8ff45e1ef9f6dc8b0b3eb35 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:43:38 +0100
Subject: [PATCH 53/61] lxd/device/device/utils/network: Use default project
 for veth route functions

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/device/device_utils_network.go | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/lxd/device/device_utils_network.go b/lxd/device/device_utils_network.go
index 9556bfda9c..e36e1b81ba 100644
--- a/lxd/device/device_utils_network.go
+++ b/lxd/device/device_utils_network.go
@@ -17,6 +17,7 @@ import (
 	"github.com/lxc/lxd/lxd/instance"
 	"github.com/lxc/lxd/lxd/instance/instancetype"
 	"github.com/lxc/lxd/lxd/network"
+	"github.com/lxc/lxd/lxd/project"
 	"github.com/lxc/lxd/lxd/revert"
 	"github.com/lxc/lxd/lxd/state"
 	"github.com/lxc/lxd/lxd/util"
@@ -377,7 +378,8 @@ func networkSetVethRoutes(s *state.State, m deviceConfig.Device) error {
 	// Decide whether the route should point to the veth parent or the bridge parent.
 	routeDev := m["host_name"]
 
-	nicType, err := nictype.NICType(s, m)
+	// Use project.Default here, as only networks in the default project can add routes on the host.
+	nicType, err := nictype.NICType(s, project.Default, m)
 	if err != nil {
 		return err
 	}
@@ -420,7 +422,9 @@ func networkSetVethRoutes(s *state.State, m deviceConfig.Device) error {
 func networkRemoveVethRoutes(s *state.State, m deviceConfig.Device) {
 	// Decide whether the route should point to the veth parent or the bridge parent
 	routeDev := m["host_name"]
-	nicType, err := nictype.NICType(s, m)
+
+	// Use project.Default here, as only networks in the default project can add routes on the host.
+	nicType, err := nictype.NICType(s, project.Default, m)
 	if err != nil {
 		logger.Errorf("Failed to get NIC type for %q", m["name"])
 		return

From 59b133434f69ed947efee8f223611dbe301483fa Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:44:37 +0100
Subject: [PATCH 54/61] lxd/device/nic/bridged: Use default project for bridge
 networks

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/device/nic_bridged.go | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/lxd/device/nic_bridged.go b/lxd/device/nic_bridged.go
index b30ba66d91..0ff48fac2c 100644
--- a/lxd/device/nic_bridged.go
+++ b/lxd/device/nic_bridged.go
@@ -24,6 +24,7 @@ import (
 	"github.com/lxc/lxd/lxd/instance/instancetype"
 	"github.com/lxc/lxd/lxd/network"
 	"github.com/lxc/lxd/lxd/network/openvswitch"
+	"github.com/lxc/lxd/lxd/project"
 	"github.com/lxc/lxd/lxd/revert"
 	"github.com/lxc/lxd/lxd/util"
 	"github.com/lxc/lxd/shared"
@@ -79,7 +80,8 @@ func (d *nicBridged) validateConfig(instConf instance.ConfigReader) error {
 		}
 
 		// If network property is specified, lookup network settings and apply them to the device's config.
-		n, err := network.LoadByName(d.state, d.config["network"])
+		// project.Default is used here as bridge networks don't suppprt projects.
+		n, err := network.LoadByName(d.state, project.Default, d.config["network"])
 		if err != nil {
 			return errors.Wrapf(err, "Error loading network config for %q", d.config["network"])
 		}
@@ -489,7 +491,8 @@ func (d *nicBridged) rebuildDnsmasqEntry() error {
 	dnsmasq.ConfigMutex.Lock()
 	defer dnsmasq.ConfigMutex.Unlock()
 
-	_, dbInfo, err := d.state.Cluster.GetNetworkInAnyState(d.config["parent"])
+	// Use project.Default here as bridge networks don't support projects.
+	_, dbInfo, err := d.state.Cluster.GetNetworkInAnyState(project.Default, d.config["parent"])
 	if err != nil {
 		return err
 	}
@@ -646,7 +649,8 @@ func (d *nicBridged) setFilters() (err error) {
 	IPv6 := net.ParseIP(d.config["ipv6.address"])
 
 	// Check if the parent is managed and load config. If parent is unmanaged continue anyway.
-	n, err := network.LoadByName(d.state, d.config["parent"])
+	// project.Default is used here as bridge networks don't suppprt projects.
+	n, err := network.LoadByName(d.state, project.Default, d.config["parent"])
 	if err != nil && err != db.ErrNoSuchObject {
 		return err
 	}

From 7482fcb860dea56ebcf2c68a8013002850649e30 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:45:06 +0100
Subject: [PATCH 55/61] lxd/device/nic/macvlan: Use default project for macvlan
 networks

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/device/nic_macvlan.go | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/lxd/device/nic_macvlan.go b/lxd/device/nic_macvlan.go
index 788584f2cd..7dbfee53cb 100644
--- a/lxd/device/nic_macvlan.go
+++ b/lxd/device/nic_macvlan.go
@@ -9,6 +9,7 @@ import (
 	"github.com/lxc/lxd/lxd/instance"
 	"github.com/lxc/lxd/lxd/instance/instancetype"
 	"github.com/lxc/lxd/lxd/network"
+	"github.com/lxc/lxd/lxd/project"
 	"github.com/lxc/lxd/lxd/revert"
 	"github.com/lxc/lxd/shared"
 	"github.com/lxc/lxd/shared/api"
@@ -49,7 +50,8 @@ func (d *nicMACVLAN) validateConfig(instConf instance.ConfigReader) error {
 		}
 
 		// If network property is specified, lookup network settings and apply them to the device's config.
-		n, err := network.LoadByName(d.state, d.config["network"])
+		// project.Default is used here as macvlan networks don't suppprt projects.
+		n, err := network.LoadByName(d.state, project.Default, d.config["network"])
 		if err != nil {
 			return errors.Wrapf(err, "Error loading network config for %q", d.config["network"])
 		}

From 30e09647d2b3eb7750973de1bed8da0e1aa24d8a Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:45:44 +0100
Subject: [PATCH 56/61] lxd/device/nic/ovn: Load parent network's project from
 instance's project

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/device/nic_ovn.go | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/lxd/device/nic_ovn.go b/lxd/device/nic_ovn.go
index 4b6db95bcd..31401bf6e1 100644
--- a/lxd/device/nic_ovn.go
+++ b/lxd/device/nic_ovn.go
@@ -14,6 +14,7 @@ import (
 	"github.com/lxc/lxd/lxd/instance/instancetype"
 	"github.com/lxc/lxd/lxd/network"
 	"github.com/lxc/lxd/lxd/network/openvswitch"
+	"github.com/lxc/lxd/lxd/project"
 	"github.com/lxc/lxd/lxd/revert"
 	"github.com/lxc/lxd/lxd/util"
 	"github.com/lxc/lxd/shared"
@@ -57,8 +58,14 @@ func (d *nicOVN) validateConfig(instConf instance.ConfigReader) error {
 		"boot.priority",
 	}
 
+	// The NIC's network may be a non-default project, so lookup project and get network's project name.
+	networkProjectName, err := project.NetworkProject(d.state.Cluster, instConf.Project())
+	if err != nil {
+		return errors.Wrapf(err, "Failed loading network project name")
+	}
+
 	// Lookup network settings and apply them to the device's config.
-	n, err := network.LoadByName(d.state, d.config["network"])
+	n, err := network.LoadByName(d.state, networkProjectName, d.config["network"])
 	if err != nil {
 		return errors.Wrapf(err, "Error loading network config for %q", d.config["network"])
 	}

From bab633c16dabc93f09012a6a5408ab475e8fb3b0 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:46:09 +0100
Subject: [PATCH 57/61] lxd/device/nic/sriov: Use default project for parent
 network

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/device/nic_sriov.go | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/lxd/device/nic_sriov.go b/lxd/device/nic_sriov.go
index 6bfcf3bd4d..aa843ecd05 100644
--- a/lxd/device/nic_sriov.go
+++ b/lxd/device/nic_sriov.go
@@ -18,6 +18,7 @@ import (
 	"github.com/lxc/lxd/lxd/instance"
 	"github.com/lxc/lxd/lxd/instance/instancetype"
 	"github.com/lxc/lxd/lxd/network"
+	"github.com/lxc/lxd/lxd/project"
 	"github.com/lxc/lxd/lxd/revert"
 	"github.com/lxc/lxd/lxd/util"
 	"github.com/lxc/lxd/shared"
@@ -59,7 +60,8 @@ func (d *nicSRIOV) validateConfig(instConf instance.ConfigReader) error {
 		}
 
 		// If network property is specified, lookup network settings and apply them to the device's config.
-		n, err := network.LoadByName(d.state, d.config["network"])
+		// project.Default is used here as macvlan networks don't suppprt projects.
+		n, err := network.LoadByName(d.state, project.Default, d.config["network"])
 		if err != nil {
 			return errors.Wrapf(err, "Error loading network config for %q", d.config["network"])
 		}

From 2ad22452cd994f10b60cda777c88022d8633f4f9 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 10:47:46 +0100
Subject: [PATCH 58/61] lxd/device/proxy: NICType project usage

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/device/proxy.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lxd/device/proxy.go b/lxd/device/proxy.go
index 9d3420622e..9e2799cfd6 100644
--- a/lxd/device/proxy.go
+++ b/lxd/device/proxy.go
@@ -341,7 +341,7 @@ func (d *proxy) setupNAT() error {
 			continue
 		}
 
-		nicType, err := nictype.NICType(d.state, devConfig)
+		nicType, err := nictype.NICType(d.state, d.inst.Project(), devConfig)
 		if err != nil {
 			return err
 		}

From 87a46efe7de6fdd421585c6ee598e734e3dda8b2 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 24 Aug 2020 11:23:34 +0100
Subject: [PATCH 59/61] lxd/api/cluster: Adds project support for networks

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/api_cluster.go | 140 ++++++++++++++++++++++++++++++++-------------
 1 file changed, 99 insertions(+), 41 deletions(-)

diff --git a/lxd/api_cluster.go b/lxd/api_cluster.go
index 81cdd258eb..bf50789610 100644
--- a/lxd/api_cluster.go
+++ b/lxd/api_cluster.go
@@ -417,18 +417,37 @@ func clusterPutJoin(d *Daemon, req api.ClusterPut) response.Response {
 			pools = append(pools, *pool)
 		}
 
-		networks := []api.Network{}
-		networkNames, err := d.cluster.GetNetworks()
-		if err != nil && err != db.ErrNoSuchObject {
+		// Get a list of projects for networks.
+		var networkProjectNames []string
+
+		err = d.cluster.Transaction(func(tx *db.ClusterTx) error {
+			networkProjectNames, err = tx.GetProjectNames()
 			return err
+		})
+		if err != nil {
+			return errors.Wrapf(err, "Failed to load projects for networks")
 		}
 
-		for _, name := range networkNames {
-			_, network, err := d.cluster.GetNetworkInAnyState(name)
-			if err != nil {
+		networks := []internalClusterPostNetwork{}
+		for _, networkProjectName := range networkProjectNames {
+			networkNames, err := d.cluster.GetNetworks(networkProjectName)
+			if err != nil && err != db.ErrNoSuchObject {
 				return err
 			}
-			networks = append(networks, *network)
+
+			for _, name := range networkNames {
+				_, network, err := d.cluster.GetNetworkInAnyState(networkProjectName, name)
+				if err != nil {
+					return err
+				}
+
+				internalNetwork := internalClusterPostNetwork{
+					Network: *network,
+					Project: networkProjectName,
+				}
+
+				networks = append(networks, internalNetwork)
+			}
 		}
 
 		// Now request for this node to be added to the list of cluster nodes.
@@ -807,7 +826,7 @@ func clusterInitMember(d, client lxd.InstanceServer, memberConfig []api.ClusterM
 func clusterAcceptMember(
 	client lxd.InstanceServer,
 	name, address string, schema, apiExt int,
-	pools []api.StoragePool, networks []api.Network) (*internalClusterPostAcceptResponse, error) {
+	pools []api.StoragePool, networks []internalClusterPostNetwork) (*internalClusterPostAcceptResponse, error) {
 
 	architecture, err := osarch.ArchitectureGetLocalID()
 	if err != nil {
@@ -1031,16 +1050,29 @@ func clusterNodeDelete(d *Daemon, r *http.Request) response.Response {
 			return response.SmartError(err)
 		}
 
-		networks, err := d.cluster.GetNetworks()
+		// Get a list of projects for networks.
+		var networkProjectNames []string
+
+		err = d.cluster.Transaction(func(tx *db.ClusterTx) error {
+			networkProjectNames, err = tx.GetProjectNames()
+			return err
+		})
 		if err != nil {
-			return response.SmartError(err)
+			return response.SmartError(errors.Wrapf(err, "Failed to load projects for networks"))
 		}
 
-		for _, name := range networks {
-			err := client.DeleteNetwork(name)
+		for _, networkProjectName := range networkProjectNames {
+			networks, err := d.cluster.GetNetworks(networkProjectName)
 			if err != nil {
 				return response.SmartError(err)
 			}
+
+			for _, name := range networks {
+				err := client.UseProject(networkProjectName).DeleteNetwork(name)
+				if err != nil {
+					return response.SmartError(err)
+				}
+			}
 		}
 
 		// Delete all the pools on this node
@@ -1153,13 +1185,19 @@ func internalClusterPostAccept(d *Daemon, r *http.Request) response.Response {
 
 // A request for the /internal/cluster/accept endpoint.
 type internalClusterPostAcceptRequest struct {
-	Name         string            `json:"name" yaml:"name"`
-	Address      string            `json:"address" yaml:"address"`
-	Schema       int               `json:"schema" yaml:"schema"`
-	API          int               `json:"api" yaml:"api"`
-	StoragePools []api.StoragePool `json:"storage_pools" yaml:"storage_pools"`
-	Networks     []api.Network     `json:"networks" yaml:"networks"`
-	Architecture int               `json:"architecture" yaml:"architecture"`
+	Name         string                       `json:"name" yaml:"name"`
+	Address      string                       `json:"address" yaml:"address"`
+	Schema       int                          `json:"schema" yaml:"schema"`
+	API          int                          `json:"api" yaml:"api"`
+	StoragePools []api.StoragePool            `json:"storage_pools" yaml:"storage_pools"`
+	Networks     []internalClusterPostNetwork `json:"networks" yaml:"networks"`
+	Architecture int                          `json:"architecture" yaml:"architecture"`
+}
+
+type internalClusterPostNetwork struct {
+	api.Network
+
+	Project string
 }
 
 // A Response for the /internal/cluster/accept endpoint.
@@ -1508,34 +1546,54 @@ func clusterCheckStoragePoolsMatch(cluster *db.Cluster, reqPools []api.StoragePo
 	return nil
 }
 
-func clusterCheckNetworksMatch(cluster *db.Cluster, reqNetworks []api.Network) error {
-	networkNames, err := cluster.GetNonPendingNetworks()
-	if err != nil && err != db.ErrNoSuchObject {
+func clusterCheckNetworksMatch(cluster *db.Cluster, reqNetworks []internalClusterPostNetwork) error {
+	var err error
+
+	// Get a list of projects for networks.
+	var networkProjectNames []string
+
+	err = cluster.Transaction(func(tx *db.ClusterTx) error {
+		networkProjectNames, err = tx.GetProjectNames()
 		return err
+	})
+	if err != nil {
+		return errors.Wrapf(err, "Failed to load projects for networks")
 	}
-	for _, name := range networkNames {
-		found := false
-		for _, reqNetwork := range reqNetworks {
-			if reqNetwork.Name != name {
-				continue
-			}
-			found = true
-			_, network, err := cluster.GetNetworkInAnyState(name)
-			if err != nil {
-				return err
+
+	for _, networkProjectName := range networkProjectNames {
+		networkNames, err := cluster.GetNonPendingNetworks(networkProjectName)
+		if err != nil && err != db.ErrNoSuchObject {
+			return err
+		}
+
+		for _, networkName := range networkNames {
+			found := false
+			for _, reqNetwork := range reqNetworks {
+				if reqNetwork.Name != networkName || reqNetwork.Project != networkProjectName {
+					continue
+				}
+
+				found = true
+				_, network, err := cluster.GetNetworkInAnyState(networkProjectName, networkName)
+				if err != nil {
+					return err
+				}
+
+				// Exclude the keys which are node-specific.
+				exclude := db.NodeSpecificNetworkConfig
+				err = util.CompareConfigs(network.Config, reqNetwork.Config, exclude)
+				if err != nil {
+					return errors.Wrapf(err, "Mismatching config for network %q in project %q", networkName, networkProjectName)
+				}
+				break
 			}
-			// Exclude the keys which are node-specific.
-			exclude := db.NodeSpecificNetworkConfig
-			err = util.CompareConfigs(network.Config, reqNetwork.Config, exclude)
-			if err != nil {
-				return fmt.Errorf("Mismatching config for network %s: %v", name, err)
+
+			if !found {
+				return fmt.Errorf("Missing network %q in project %q", networkName, networkProjectName)
 			}
-			break
-		}
-		if !found {
-			return fmt.Errorf("Missing network %s", name)
 		}
 	}
+
 	return nil
 }
 

From 74d4b7b0dd7462f4c9e582f9b4dafcbe1decd794 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 25 Aug 2020 10:02:38 +0100
Subject: [PATCH 60/61] lxd/network/driver/common: Send project when notifying
 nodes of network changes

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

diff --git a/lxd/network/driver_common.go b/lxd/network/driver_common.go
index 3fc229f56b..e78e80209d 100644
--- a/lxd/network/driver_common.go
+++ b/lxd/network/driver_common.go
@@ -251,7 +251,7 @@ func (n *common) update(applyNetwork api.NetworkPut, targetNode string, clusterN
 			}
 
 			err = notifier(func(client lxd.InstanceServer) error {
-				return client.UpdateNetwork(n.name, sendNetwork, "")
+				return client.UseProject(n.project).UpdateNetwork(n.name, sendNetwork, "")
 			})
 			if err != nil {
 				return err
@@ -353,7 +353,7 @@ func (n *common) delete(clusterNotification bool) error {
 			return err
 		}
 		err = notifier(func(client lxd.InstanceServer) error {
-			return client.DeleteNetwork(n.name)
+			return client.UseProject(n.project).DeleteNetwork(n.name)
 		})
 		if err != nil {
 			return err

From 66984582f591bfb6e71fafea419d566b2e5e1561 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 25 Aug 2020 10:03:10 +0100
Subject: [PATCH 61/61] lxd/networks: Send project when creating network on
 remote node

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

diff --git a/lxd/networks.go b/lxd/networks.go
index c10dd541db..4b096a37f9 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -363,7 +363,7 @@ func networksPostCluster(d *Daemon, projectName string, req api.NetworksPost) er
 			nodeReq.Config[key] = value
 		}
 
-		return client.CreateNetwork(nodeReq)
+		return client.UseProject(projectName).CreateNetwork(nodeReq)
 	})
 	if err != nil {
 		return err


More information about the lxc-devel mailing list