[lxc-devel] [lxd/master] Network: Adds state column to networks_nodes table

tomponline on Github lxc-bot at linuxcontainers.org
Mon Nov 23 17:34:36 UTC 2020


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 553 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20201123/8b5875c9/attachment-0001.bin>
-------------- next part --------------
From 4694b1a5955b131848b26db418720f126cc8b6e1 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Fri, 20 Nov 2020 11:44:34 +0000
Subject: [PATCH 01/35] lxd/storage/pools/utils: Updates comment and error for
 storagePoolCreateLocal

Makes more accurate.

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

diff --git a/lxd/storage_pools_utils.go b/lxd/storage_pools_utils.go
index 57cc08b74f..6ea29eb5c2 100644
--- a/lxd/storage_pools_utils.go
+++ b/lxd/storage_pools_utils.go
@@ -3,6 +3,8 @@ package main
 import (
 	"fmt"
 
+	"github.com/pkg/errors"
+
 	"github.com/lxc/lxd/lxd/state"
 	storagePools "github.com/lxc/lxd/lxd/storage"
 	"github.com/lxc/lxd/shared"
@@ -95,7 +97,7 @@ func storagePoolCreateGlobal(state *state.State, req api.StoragePoolsPost) error
 	return nil
 }
 
-// This performs all non-db related work needed to create the pool.
+// This performs local pool setup and updates DB record if config was changed during pool setup.
 func storagePoolCreateLocal(state *state.State, id int64, req api.StoragePoolsPost, isNotification bool) (map[string]string, error) {
 	tryUndo := true
 
@@ -145,7 +147,7 @@ func storagePoolCreateLocal(state *state.State, id int64, req api.StoragePoolsPo
 		// Create the database entry for the storage pool.
 		err = state.Cluster.UpdateStoragePool(req.Name, req.Description, updatedConfig)
 		if err != nil {
-			return nil, fmt.Errorf("Error inserting %s into database: %s", req.Name, err)
+			return nil, errors.Wrapf(err, "Error updating storage pool config after local create for %q", req.Name)
 		}
 	}
 

From f30c166a542ef2fa98a7d570dc2850b668e6c2d6 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Fri, 20 Nov 2020 11:46:21 +0000
Subject: [PATCH 02/35] lxd/storage/pools: Error quoting

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

diff --git a/lxd/storage_pools.go b/lxd/storage_pools.go
index c631d7cbc7..2e3631e676 100644
--- a/lxd/storage_pools.go
+++ b/lxd/storage_pools.go
@@ -182,7 +182,7 @@ func storagePoolsPost(d *Daemon, r *http.Request) response.Response {
 	// storage config are the ones in StoragePoolNodeConfigKeys.
 	for key := range req.Config {
 		if !shared.StringInSlice(key, db.StoragePoolNodeConfigKeys) {
-			return response.SmartError(fmt.Errorf("Config key '%s' may not be used as node-specific key", key))
+			return response.SmartError(fmt.Errorf("Config key %q may not be used as node-specific key", key))
 		}
 	}
 
@@ -196,7 +196,7 @@ func storagePoolsPost(d *Daemon, r *http.Request) response.Response {
 	})
 	if err != nil {
 		if err == db.ErrAlreadyDefined {
-			return response.BadRequest(fmt.Errorf("The storage pool already defined on node %s", targetNode))
+			return response.BadRequest(fmt.Errorf("The storage pool already defined on node %q", targetNode))
 		}
 
 		return response.SmartError(err)
@@ -209,7 +209,7 @@ func storagePoolsPostCluster(d *Daemon, req api.StoragePoolsPost) error {
 	// Check that no node-specific config key has been defined.
 	for key := range req.Config {
 		if shared.StringInSlice(key, db.StoragePoolNodeConfigKeys) {
-			return fmt.Errorf("Config key '%s' is node-specific", key)
+			return fmt.Errorf("Config key %q is node-specific", key)
 		}
 	}
 
@@ -525,7 +525,7 @@ func storagePoolPatch(d *Daemon, r *http.Request) response.Response {
 func storagePoolValidateClusterConfig(reqConfig map[string]string) error {
 	for key := range reqConfig {
 		if shared.StringInSlice(key, db.StoragePoolNodeConfigKeys) {
-			return fmt.Errorf("node-specific config key %s can't be changed", key)
+			return fmt.Errorf("Node-specific config key %q can't be changed", key)
 		}
 	}
 	return nil

From e2481e8abce41ebc3ddee524aa4164863b102715 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Fri, 20 Nov 2020 14:37:47 +0000
Subject: [PATCH 03/35] lxd/db/cluster: Adds state column to
 storage_pools_nodes and networks_nodes table and set existing rows to state=1
 (created)

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/db/cluster/schema.go |  4 +++-
 lxd/db/cluster/update.go | 13 +++++++++++++
 2 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/lxd/db/cluster/schema.go b/lxd/db/cluster/schema.go
index 3e9227899b..8d0c38bdb8 100644
--- a/lxd/db/cluster/schema.go
+++ b/lxd/db/cluster/schema.go
@@ -299,6 +299,7 @@ CREATE TABLE "networks_nodes" (
     id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
     network_id INTEGER NOT NULL,
     node_id INTEGER NOT NULL,
+    state INTEGER NOT NULL DEFAULT 0,
     UNIQUE (network_id, node_id),
     FOREIGN KEY (network_id) REFERENCES "networks" (id) ON DELETE CASCADE,
     FOREIGN KEY (node_id) REFERENCES nodes (id) ON DELETE CASCADE
@@ -489,6 +490,7 @@ CREATE TABLE storage_pools_nodes (
     id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
     storage_pool_id INTEGER NOT NULL,
     node_id INTEGER NOT NULL,
+    state INTEGER NOT NULL DEFAULT 0,
     UNIQUE (storage_pool_id, node_id),
     FOREIGN KEY (storage_pool_id) REFERENCES storage_pools (id) ON DELETE CASCADE,
     FOREIGN KEY (node_id) REFERENCES nodes (id) ON DELETE CASCADE
@@ -589,5 +591,5 @@ CREATE TABLE storage_volumes_snapshots_config (
     UNIQUE (storage_volume_snapshot_id, key)
 );
 
-INSERT INTO schema (version, updated_at) VALUES (39, strftime("%s"))
+INSERT INTO schema (version, updated_at) VALUES (40, strftime("%s"))
 `
diff --git a/lxd/db/cluster/update.go b/lxd/db/cluster/update.go
index 750097c693..cfb300b95d 100644
--- a/lxd/db/cluster/update.go
+++ b/lxd/db/cluster/update.go
@@ -76,6 +76,19 @@ var updates = map[int]schema.Update{
 	37: updateFromV36,
 	38: updateFromV37,
 	39: updateFromV38,
+	40: updateFromV39,
+}
+
+// Add state column to storage_pools_nodes and networks_nodes tables. Set existing row's state to 1 ("created").
+func updateFromV39(tx *sql.Tx) error {
+	stmt := `
+		ALTER TABLE storage_pools_nodes ADD COLUMN state INTEGER NOT NULL DEFAULT 0;
+		UPDATE storage_pools_nodes SET state = 1;
+		ALTER TABLE networks_nodes ADD COLUMN state INTEGER NOT NULL DEFAULT 0;
+		UPDATE networks_nodes SET state = 1;
+	`
+	_, err := tx.Exec(stmt)
+	return err
 }
 
 // Add storage_volumes_backups table.

From 530e9e2db125ab06784a14386241d84ae01f18cd Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 23 Nov 2020 11:17:12 +0000
Subject: [PATCH 04/35] lxd/db/networks: Populate node state column in
 NetworkNodeJoin

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

diff --git a/lxd/db/networks.go b/lxd/db/networks.go
index b2549cabd5..c0fecfc5ce 100644
--- a/lxd/db/networks.go
+++ b/lxd/db/networks.go
@@ -171,8 +171,9 @@ func (c *ClusterTx) CreateNetworkConfig(networkID, nodeID int64, config map[stri
 // assume that the relevant network has already been created on the joining node,
 // and we just need to track it.
 func (c *ClusterTx) NetworkNodeJoin(networkID, nodeID int64) error {
-	columns := []string{"network_id", "node_id"}
-	values := []interface{}{networkID, nodeID}
+	columns := []string{"network_id", "node_id", "state"}
+	// Create network node with "created" state as we expect the network to already be setup.
+	values := []interface{}{networkID, nodeID, networkCreated}
 	_, err := query.UpsertObject(c.tx, "networks_nodes", columns, values)
 	return err
 }

From 2bd4e3b69bb0779f015d0ec7581c8658c110a705 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 23 Nov 2020 11:17:36 +0000
Subject: [PATCH 05/35] lxd/db/networks: Populate node state column in
 CreatePendingNetwork

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

diff --git a/lxd/db/networks.go b/lxd/db/networks.go
index c0fecfc5ce..f4964a99da 100644
--- a/lxd/db/networks.go
+++ b/lxd/db/networks.go
@@ -299,9 +299,9 @@ func (c *ClusterTx) CreatePendingNetwork(node string, projectName string, name s
 		return ErrAlreadyDefined
 	}
 
-	// Insert the node-specific configuration.
-	columns := []string{"network_id", "node_id"}
-	values := []interface{}{networkID, nodeInfo.ID}
+	// Insert the node-specific configuration with state "pending".
+	columns := []string{"network_id", "node_id", "state"}
+	values := []interface{}{networkID, nodeInfo.ID, networkPending}
 	_, err = query.UpsertObject(c.tx, "networks_nodes", columns, values)
 	if err != nil {
 		return err

From d18d739b06fa9cc0e29fb6a6ccdc21c9b2aca1a3 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 23 Nov 2020 11:18:01 +0000
Subject: [PATCH 06/35] lxd/db/networks: Adds networkNodeState and
 NetworkNodeCreated functiond

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

diff --git a/lxd/db/networks.go b/lxd/db/networks.go
index f4964a99da..ba74ef7d72 100644
--- a/lxd/db/networks.go
+++ b/lxd/db/networks.go
@@ -341,6 +341,29 @@ func (c *ClusterTx) networkState(project string, name string, state int) error {
 	return nil
 }
 
+// NetworkNodeCreated sets the state of the given network for the local member to "Created".
+func (c *ClusterTx) NetworkNodeCreated(networkID int64) error {
+	return c.networkNodeState(networkID, networkCreated)
+}
+
+// networkNodeState updates the network member state for the local member and specified network ID.
+func (c *ClusterTx) networkNodeState(networkID int64, state int) error {
+	stmt := "UPDATE networks_nodes SET state=? WHERE network_id = ? and node_id = ?"
+	result, err := c.tx.Exec(stmt, state, networkID, c.nodeID)
+	if err != nil {
+		return err
+	}
+	n, err := result.RowsAffected()
+	if err != nil {
+		return err
+	}
+	if n != 1 {
+		return ErrNoSuchObject
+	}
+
+	return nil
+}
+
 // UpdateNetwork updates the network with the given ID.
 func (c *ClusterTx) UpdateNetwork(id int64, description string, config map[string]string) error {
 	err := updateNetworkDescription(c.tx, id, description)

From 2d1c8354a520550b546528aadbc5891938631bfb Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 23 Nov 2020 11:18:37 +0000
Subject: [PATCH 07/35] lxd/db/networks: Comments

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

diff --git a/lxd/db/networks.go b/lxd/db/networks.go
index ba74ef7d72..b06411e610 100644
--- a/lxd/db/networks.go
+++ b/lxd/db/networks.go
@@ -456,15 +456,12 @@ const (
 	NetworkTypePhysical                    // Network type physical.
 )
 
-// GetNetworkInAnyState returns the network with the given name.
-//
-// The network can be in any state.
+// GetNetworkInAnyState returns the network with the given name. The network can be in any state.
 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.
+// Get the network with the given name. If onlyCreated is true, only return networks in the created state.
 func (c *Cluster) getNetwork(project string, name string, onlyCreated bool) (int64, *api.Network, error) {
 	description := sql.NullString{}
 	id := int64(-1)
@@ -562,8 +559,7 @@ func (c *Cluster) networkNodes(networkID int64) ([]string, error) {
 	return nodes, nil
 }
 
-// GetNetworkWithInterface returns the network associated with the interface with
-// the given name.
+// GetNetworkWithInterface returns the network associated with the interface with the given name.
 func (c *Cluster) GetNetworkWithInterface(devName string) (int64, *api.Network, error) {
 	id := int64(-1)
 	name := ""
@@ -656,6 +652,7 @@ func (c *Cluster) getNetworkConfig(id int64) (map[string]string, error) {
 func (c *Cluster) CreateNetwork(projectName string, name string, description string, netType NetworkType, config map[string]string) (int64, error) {
 	var id int64
 	err := c.Transaction(func(tx *ClusterTx) error {
+		// Insert a new network record with state "created".
 		result, err := tx.tx.Exec("INSERT INTO networks (project_id, name, description, state, type) VALUES ((SELECT id FROM projects WHERE name = ?), ?, ?, ?, ?)",
 			projectName, name, description, networkCreated, netType)
 		if err != nil {

From ab72fd14c9bd1f32a8262651daac3b85f1bb2323 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 23 Nov 2020 11:18:49 +0000
Subject: [PATCH 08/35] lxd/db/networks: Populate node state column in
 CreateNetwork

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

diff --git a/lxd/db/networks.go b/lxd/db/networks.go
index b06411e610..a879a23728 100644
--- a/lxd/db/networks.go
+++ b/lxd/db/networks.go
@@ -664,9 +664,9 @@ func (c *Cluster) CreateNetwork(projectName string, name string, description str
 			return err
 		}
 
-		// Insert a node-specific entry pointing to ourselves.
-		columns := []string{"network_id", "node_id"}
-		values := []interface{}{id, c.nodeID}
+		// Insert a node-specific entry pointing to ourselves with state "pending".
+		columns := []string{"network_id", "node_id", "state"}
+		values := []interface{}{id, c.nodeID, networkPending}
 		_, err = query.UpsertObject(tx.tx, "networks_nodes", columns, values)
 		if err != nil {
 			return err

From 9ef2f1f485c6ae85e5f31345541825618d893233 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 23 Nov 2020 11:19:31 +0000
Subject: [PATCH 09/35] lxd/network/driver: Remove check that prevents starting
 network in pending state

This is needed as we now need to check if the network can start OK before marking the network created.

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/network/driver_bridge.go   | 4 ----
 lxd/network/driver_macvlan.go  | 6 ------
 lxd/network/driver_ovn.go      | 4 ----
 lxd/network/driver_physical.go | 4 ----
 lxd/network/driver_sriov.go    | 6 ------
 5 files changed, 24 deletions(-)

diff --git a/lxd/network/driver_bridge.go b/lxd/network/driver_bridge.go
index ec30df98ca..8014bdb256 100644
--- a/lxd/network/driver_bridge.go
+++ b/lxd/network/driver_bridge.go
@@ -487,10 +487,6 @@ func (n *bridge) setup(oldConfig map[string]string) error {
 
 	n.logger.Debug("Setting up network")
 
-	if n.status == api.NetworkStatusPending {
-		return fmt.Errorf("Cannot start pending network")
-	}
-
 	// Create directory.
 	if !shared.PathExists(shared.VarPath("networks", n.name)) {
 		err := os.MkdirAll(shared.VarPath("networks", n.name), 0711)
diff --git a/lxd/network/driver_macvlan.go b/lxd/network/driver_macvlan.go
index 09c15a174f..9c9a159cc5 100644
--- a/lxd/network/driver_macvlan.go
+++ b/lxd/network/driver_macvlan.go
@@ -1,8 +1,6 @@
 package network
 
 import (
-	"fmt"
-
 	"github.com/lxc/lxd/lxd/cluster"
 	"github.com/lxc/lxd/lxd/db"
 	"github.com/lxc/lxd/lxd/revert"
@@ -67,10 +65,6 @@ func (n *macvlan) Rename(newName string) error {
 func (n *macvlan) Start() error {
 	n.logger.Debug("Start")
 
-	if n.status == api.NetworkStatusPending {
-		return fmt.Errorf("Cannot start pending network")
-	}
-
 	return nil
 }
 
diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index 03dd522eb7..097aaeb011 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -1836,10 +1836,6 @@ func (n *ovn) Rename(newName string) error {
 func (n *ovn) Start() error {
 	n.logger.Debug("Start")
 
-	if n.status == api.NetworkStatusPending {
-		return fmt.Errorf("Cannot start pending network")
-	}
-
 	// Add local node's OVS chassis ID to logical chassis group.
 	err := n.addChassisGroupEntry()
 	if err != nil {
diff --git a/lxd/network/driver_physical.go b/lxd/network/driver_physical.go
index 3d24584924..4ee105c8f2 100644
--- a/lxd/network/driver_physical.go
+++ b/lxd/network/driver_physical.go
@@ -143,10 +143,6 @@ func (n *physical) Rename(newName string) error {
 func (n *physical) Start() error {
 	n.logger.Debug("Start")
 
-	if n.status == api.NetworkStatusPending {
-		return fmt.Errorf("Cannot start pending network")
-	}
-
 	revert := revert.New()
 	defer revert.Fail()
 
diff --git a/lxd/network/driver_sriov.go b/lxd/network/driver_sriov.go
index 299e2dd2df..6a745cbb18 100644
--- a/lxd/network/driver_sriov.go
+++ b/lxd/network/driver_sriov.go
@@ -1,8 +1,6 @@
 package network
 
 import (
-	"fmt"
-
 	"github.com/lxc/lxd/lxd/cluster"
 	"github.com/lxc/lxd/lxd/db"
 	"github.com/lxc/lxd/lxd/revert"
@@ -67,10 +65,6 @@ func (n *sriov) Rename(newName string) error {
 func (n *sriov) Start() error {
 	n.logger.Debug("Start")
 
-	if n.status == api.NetworkStatusPending {
-		return fmt.Errorf("Cannot start pending network")
-	}
-
 	return nil
 }
 

From 9d4fd2e90d2bf3b984aa184ba072e4844117eef5 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 23 Nov 2020 11:20:38 +0000
Subject: [PATCH 10/35] lxd/networks: Whitespace

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

diff --git a/lxd/networks.go b/lxd/networks.go
index 09ea358629..a4fec44216 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -279,7 +279,6 @@ func networksPost(d *Daemon, r *http.Request) response.Response {
 	}
 
 	// Non-clustered network creation.
-
 	networks, err := d.cluster.GetNetworks(projectName)
 	if err != nil {
 		return response.InternalError(err)

From 46040063f8c01765ce14fe5d84b785ba240c55dd Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 23 Nov 2020 17:02:25 +0000
Subject: [PATCH 11/35] lxd/network/network/interface: Updates init to take
 api.Network and network nodes map

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 b9ba1e89d1..1df3fe001c 100644
--- a/lxd/network/network_interface.go
+++ b/lxd/network/network_interface.go
@@ -24,7 +24,7 @@ type Network interface {
 	Type
 
 	// Load.
-	init(state *state.State, id int64, projectName string, name string, netType string, description string, config map[string]string, status string)
+	init(state *state.State, id int64, projectName string, netInfo *api.Network, netNodes map[int64]db.NetworkNode)
 
 	// Config.
 	Validate(config map[string]string) error

From 96da9ddd37fcfee0e45f9baf7fe1bb8c666360c5 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 23 Nov 2020 17:02:57 +0000
Subject: [PATCH 12/35] lxd/network/network/interface: Adds LocalStatus

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

diff --git a/lxd/network/network_interface.go b/lxd/network/network_interface.go
index 1df3fe001c..c5f60ca4ca 100644
--- a/lxd/network/network_interface.go
+++ b/lxd/network/network_interface.go
@@ -32,6 +32,7 @@ type Network interface {
 	Name() string
 	Description() string
 	Status() string
+	LocalStatus() string
 	Config() map[string]string
 	IsUsed() (bool, error)
 	DHCPv4Subnet() *net.IPNet

From a897d04a10651bce3cbac3933d2d873d792ed6d1 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 23 Nov 2020 17:03:26 +0000
Subject: [PATCH 13/35] lxd/network/network/load: Updates LoadByName to pass
 network nodes from s.Cluster.GetNetworkInAnyState to init()

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

diff --git a/lxd/network/network_load.go b/lxd/network/network_load.go
index 7098181a57..0b165b63cc 100644
--- a/lxd/network/network_load.go
+++ b/lxd/network/network_load.go
@@ -26,7 +26,7 @@ func LoadByType(driverType string) (Type, error) {
 
 // LoadByName loads an instantiated network from the database by project and name.
 func LoadByName(s *state.State, projectName string, name string) (Network, error) {
-	id, netInfo, err := s.Cluster.GetNetworkInAnyState(projectName, name)
+	id, netInfo, netNodes, err := s.Cluster.GetNetworkInAnyState(projectName, name)
 	if err != nil {
 		return nil, err
 	}
@@ -37,7 +37,7 @@ func LoadByName(s *state.State, projectName string, name string) (Network, error
 	}
 
 	n := driverFunc()
-	n.init(s, id, projectName, name, netInfo.Type, netInfo.Description, netInfo.Config, netInfo.Status)
+	n.init(s, id, projectName, netInfo, netNodes)
 
 	return n, nil
 }

From 2d44f1ce305fb3a9d8970486ed6d19b80c988b81 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 23 Nov 2020 17:05:56 +0000
Subject: [PATCH 14/35] lxd/db/networks: Adds NetworkState type and uses it in
 place of int

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

diff --git a/lxd/db/networks.go b/lxd/db/networks.go
index a879a23728..66e57968d2 100644
--- a/lxd/db/networks.go
+++ b/lxd/db/networks.go
@@ -94,7 +94,7 @@ func (c *ClusterTx) GetNonPendingNetworks() (map[string]map[int64]api.Network, e
 		var projectName string
 		var networkID int64
 		var networkType NetworkType
-		var networkState int
+		var networkState NetworkState
 		var network api.Network
 
 		err := rows.Scan(&projectName, &networkID, &network.Name, &network.Description, &networkType, &networkState)
@@ -230,7 +230,7 @@ func (c *ClusterTx) CreatePendingNetwork(node string, projectName string, name s
 	// First check if a network with the given name exists, and, if so, that it's in the pending state.
 	network := struct {
 		id      int64
-		state   int
+		state   NetworkState
 		netType NetworkType
 	}{}
 
@@ -325,7 +325,7 @@ func (c *ClusterTx) NetworkErrored(project string, name string) error {
 	return c.networkState(project, name, networkErrored)
 }
 
-func (c *ClusterTx) networkState(project string, name string, state int) error {
+func (c *ClusterTx) networkState(project string, name string, state NetworkState) 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 {
@@ -347,7 +347,7 @@ func (c *ClusterTx) NetworkNodeCreated(networkID int64) error {
 }
 
 // networkNodeState updates the network member state for the local member and specified network ID.
-func (c *ClusterTx) networkNodeState(networkID int64, state int) error {
+func (c *ClusterTx) networkNodeState(networkID int64, state NetworkState) error {
 	stmt := "UPDATE networks_nodes SET state=? WHERE network_id = ? and node_id = ?"
 	result, err := c.tx.Exec(stmt, state, networkID, c.nodeID)
 	if err != nil {
@@ -437,11 +437,14 @@ func (c *Cluster) networks(project string, where string, args ...interface{}) ([
 	return response, nil
 }
 
+// NetworkState indicates the state of the network or network node.
+type NetworkState int
+
 // Network state.
 const (
-	networkPending int = iota // Network defined but not yet created.
-	networkCreated            // Network created on all nodes.
-	networkErrored            // Network creation failed on some nodes
+	networkPending NetworkState = iota // Network defined but not yet created.
+	networkCreated                     // Network created on all nodes.
+	networkErrored                     // Network creation failed on some nodes
 )
 
 // NetworkType indicates type of network.
@@ -465,7 +468,7 @@ func (c *Cluster) GetNetworkInAnyState(project string, name string) (int64, *api
 func (c *Cluster) getNetwork(project string, name string, onlyCreated bool) (int64, *api.Network, error) {
 	description := sql.NullString{}
 	id := int64(-1)
-	state := 0
+	var state NetworkState
 	var netType NetworkType
 
 	q := "SELECT id, description, state, type FROM networks WHERE project_id = (SELECT id FROM projects WHERE name = ?) AND name=?"

From dad43ff1fea7efaf9be1dc2fc9dd1421c73edef8 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 23 Nov 2020 17:07:37 +0000
Subject: [PATCH 15/35] lxd/db/networks: Renames networkFillStatus to
 NetworkStateToAPIStatus

And updates usage.

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

diff --git a/lxd/db/networks.go b/lxd/db/networks.go
index 66e57968d2..9fe91ec9f7 100644
--- a/lxd/db/networks.go
+++ b/lxd/db/networks.go
@@ -103,7 +103,7 @@ func (c *ClusterTx) GetNonPendingNetworks() (map[string]map[int64]api.Network, e
 		}
 
 		// Populate Status and Type fields by converting from DB values.
-		networkFillStatus(&network, networkState)
+		network.Status = NetworkStateToAPIStatus(networkState)
 		networkFillType(&network, networkType)
 
 		if projectNetworks[projectName] != nil {
@@ -500,7 +500,7 @@ func (c *Cluster) getNetwork(project string, name string, onlyCreated bool) (int
 	network.Config = config
 
 	// Populate Status and Type fields by converting from DB values.
-	networkFillStatus(&network, state)
+	network.Status = NetworkStateToAPIStatus(state)
 	networkFillType(&network, netType)
 
 	nodes, err := c.networkNodes(id)
@@ -512,16 +512,17 @@ func (c *Cluster) getNetwork(project string, name string, onlyCreated bool) (int
 	return id, &network, nil
 }
 
-func networkFillStatus(network *api.Network, state int) {
+// NetworkStateToAPIStatus converts DB NetworkState to API status string.
+func NetworkStateToAPIStatus(state NetworkState) string {
 	switch state {
 	case networkPending:
-		network.Status = api.NetworkStatusPending
+		return api.NetworkStatusPending
 	case networkCreated:
-		network.Status = api.NetworkStatusCreated
+		return api.NetworkStatusCreated
 	case networkErrored:
-		network.Status = api.NetworkStatusErrored
+		return api.NetworkStatusErrored
 	default:
-		network.Status = api.NetworkStatusUnknown
+		return api.NetworkStatusUnknown
 	}
 }
 

From c1621affaa329c1baa3d94ea9143dc7d8c8ec3d7 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 23 Nov 2020 17:09:45 +0000
Subject: [PATCH 16/35] lxd/db/networks: Adds NetworkNode type

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

diff --git a/lxd/db/networks.go b/lxd/db/networks.go
index 9fe91ec9f7..4b1e5d3158 100644
--- a/lxd/db/networks.go
+++ b/lxd/db/networks.go
@@ -459,6 +459,13 @@ const (
 	NetworkTypePhysical                    // Network type physical.
 )
 
+// NetworkNode represents a network node.
+type NetworkNode struct {
+	ID    int64
+	Name  string
+	State NetworkState
+}
+
 // GetNetworkInAnyState returns the network with the given name. The network can be in any state.
 func (c *Cluster) GetNetworkInAnyState(project string, name string) (int64, *api.Network, error) {
 	return c.getNetwork(project, name, false)

From 44421a5f2dce648b72cfe0b33d08efa564276465 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 23 Nov 2020 17:10:11 +0000
Subject: [PATCH 17/35] lxd/db/networks: Exports NetworkNodes and updates to
 return map of NetworkNodes

Including node ID, name and state.

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

diff --git a/lxd/db/networks.go b/lxd/db/networks.go
index 4b1e5d3158..5ff4322ed5 100644
--- a/lxd/db/networks.go
+++ b/lxd/db/networks.go
@@ -384,20 +384,35 @@ func (c *ClusterTx) UpdateNetwork(id int64, description string, config map[strin
 	return nil
 }
 
-// Return the names of the nodes the given network is defined on.
-func (c *ClusterTx) networkNodes(networkID int64) ([]string, error) {
-	var err error
-	stmt := `
-	SELECT nodes.name FROM nodes
-	  JOIN networks_nodes ON networks_nodes.node_id = nodes.id
-	  WHERE networks_nodes.network_id = ?
-	`
-	nodes, err := query.SelectStrings(c.tx, stmt, networkID)
+// NetworkNodes returns the nodes keyed by node ID that the given network is defined on.
+func (c *ClusterTx) NetworkNodes(networkID int64) (map[int64]NetworkNode, error) {
+	nodes := []NetworkNode{}
+	dest := func(i int) []interface{} {
+		nodes = append(nodes, NetworkNode{})
+		return []interface{}{&nodes[i].ID, &nodes[i].Name, &nodes[i].State}
+	}
+
+	stmt, err := c.tx.Prepare(`
+		SELECT nodes.id, nodes.name, networks_nodes.state FROM nodes
+		JOIN networks_nodes ON networks_nodes.node_id = nodes.id
+		WHERE networks_nodes.network_id = ?
+	`)
+	if err != nil {
+		return nil, err
+	}
+	defer stmt.Close()
+
+	err = query.SelectObjects(stmt, dest, networkID)
 	if err != nil {
 		return nil, err
 	}
 
-	return nodes, nil
+	netNodes := map[int64]NetworkNode{}
+	for _, node := range nodes {
+		netNodes[node.ID] = node
+	}
+
+	return netNodes, nil
 }
 
 // GetNetworks returns the names of existing networks.

From f661562e100b7edda869e8c710e57709ffbf355c Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 23 Nov 2020 17:11:41 +0000
Subject: [PATCH 18/35] lxd/db/networks: Updates GetNonPendingNetworks usage of
 NetworkNodes()

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

diff --git a/lxd/db/networks.go b/lxd/db/networks.go
index 5ff4322ed5..ddeee25093 100644
--- a/lxd/db/networks.go
+++ b/lxd/db/networks.go
@@ -130,11 +130,14 @@ func (c *ClusterTx) GetNonPendingNetworks() (map[string]map[int64]api.Network, e
 
 			network.Config = networkConfig
 
-			nodes, err := c.networkNodes(networkID)
+			nodes, err := c.NetworkNodes(networkID)
 			if err != nil {
 				return nil, err
 			}
-			network.Locations = nodes
+
+			for _, node := range nodes {
+				network.Locations = append(network.Locations, node.Name)
+			}
 
 			projectNetworks[projectName][networkID] = network
 		}

From 3f2003d784e30478c71bff21848f890e6c525f90 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 23 Nov 2020 17:12:10 +0000
Subject: [PATCH 19/35] lxd/db/networks: Modifies getNetwork and
 GetNetworkInAnyState to return map of NetworkNodes for network

We run the query anyway to populate Network's Location field so cheap to return at same time.

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

diff --git a/lxd/db/networks.go b/lxd/db/networks.go
index ddeee25093..9c086d0f37 100644
--- a/lxd/db/networks.go
+++ b/lxd/db/networks.go
@@ -485,12 +485,13 @@ type NetworkNode struct {
 }
 
 // GetNetworkInAnyState returns the network with the given name. The network can be in any state.
-func (c *Cluster) GetNetworkInAnyState(project string, name string) (int64, *api.Network, error) {
+func (c *Cluster) GetNetworkInAnyState(project string, name string) (int64, *api.Network, map[int64]NetworkNode, 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(project string, name string, onlyCreated bool) (int64, *api.Network, error) {
+// Get the network with the given name. If onlyCreated is true, only return networks in the networkCreated state.
+// Also returns a map of the network's nodes keyed by node ID.
+func (c *Cluster) getNetwork(project string, name string, onlyCreated bool) (int64, *api.Network, map[int64]NetworkNode, error) {
 	description := sql.NullString{}
 	id := int64(-1)
 	var state NetworkState
@@ -506,15 +507,15 @@ func (c *Cluster) getNetwork(project string, name string, onlyCreated bool) (int
 	err := dbQueryRowScan(c, q, arg1, arg2)
 	if err != nil {
 		if err == sql.ErrNoRows {
-			return -1, nil, ErrNoSuchObject
+			return -1, nil, nil, ErrNoSuchObject
 		}
 
-		return -1, nil, err
+		return -1, nil, nil, err
 	}
 
 	config, err := c.getNetworkConfig(id)
 	if err != nil {
-		return -1, nil, err
+		return -1, nil, nil, err
 	}
 
 	network := api.Network{
@@ -528,13 +529,16 @@ func (c *Cluster) getNetwork(project string, name string, onlyCreated bool) (int
 	network.Status = NetworkStateToAPIStatus(state)
 	networkFillType(&network, netType)
 
-	nodes, err := c.networkNodes(id)
+	nodes, err := c.NetworkNodes(id)
 	if err != nil {
-		return -1, nil, err
+		return -1, nil, nil, err
 	}
-	network.Locations = nodes
 
-	return id, &network, nil
+	for _, node := range nodes {
+		network.Locations = append(network.Locations, node.Name)
+	}
+
+	return id, &network, nodes, nil
 }
 
 // NetworkStateToAPIStatus converts DB NetworkState to API status string.

From 2eecd37616e851b694c70f33ffd6bb71ad399f7e Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 23 Nov 2020 17:13:07 +0000
Subject: [PATCH 20/35] lxd/db/networks: Exports NetworkNodes

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

diff --git a/lxd/db/networks.go b/lxd/db/networks.go
index 9c086d0f37..d0fd2a14b1 100644
--- a/lxd/db/networks.go
+++ b/lxd/db/networks.go
@@ -572,13 +572,13 @@ func networkFillType(network *api.Network, netType NetworkType) {
 	}
 }
 
-// Return the names of the nodes the given network is defined on.
-func (c *Cluster) networkNodes(networkID int64) ([]string, error) {
-	var nodes []string
+// NetworkNodes returns the nodes keyed by node ID that the given network is defined on.
+func (c *Cluster) NetworkNodes(networkID int64) (map[int64]NetworkNode, error) {
+	var nodes map[int64]NetworkNode
 	var err error
 
 	err = c.Transaction(func(tx *ClusterTx) error {
-		nodes, err = tx.networkNodes(networkID)
+		nodes, err = tx.NetworkNodes(networkID)
 		if err != nil {
 			return err
 		}

From 8518b15362caf714185d4832b06d82d329b382c1 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 23 Nov 2020 17:13:49 +0000
Subject: [PATCH 21/35] lxd/db/networks: c.GetNetworkInAnyState usage

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

diff --git a/lxd/db/networks.go b/lxd/db/networks.go
index d0fd2a14b1..12866e0344 100644
--- a/lxd/db/networks.go
+++ b/lxd/db/networks.go
@@ -720,7 +720,7 @@ func (c *Cluster) CreateNetwork(projectName string, name string, description str
 
 // UpdateNetwork updates the network with the given name.
 func (c *Cluster) UpdateNetwork(project string, name, description string, config map[string]string) error {
-	id, netInfo, err := c.GetNetworkInAnyState(project, name)
+	id, netInfo, _, err := c.GetNetworkInAnyState(project, name)
 	if err != nil {
 		return err
 	}
@@ -794,7 +794,7 @@ func clearNetworkConfig(tx *sql.Tx, networkID, nodeID int64) error {
 
 // DeleteNetwork deletes the network with the given name.
 func (c *Cluster) DeleteNetwork(project string, name string) error {
-	id, _, err := c.GetNetworkInAnyState(project, name)
+	id, _, _, err := c.GetNetworkInAnyState(project, name)
 	if err != nil {
 		return err
 	}
@@ -809,7 +809,7 @@ func (c *Cluster) DeleteNetwork(project string, name string) error {
 
 // RenameNetwork renames a network.
 func (c *Cluster) RenameNetwork(project string, oldName string, newName string) error {
-	id, _, err := c.GetNetworkInAnyState(project, oldName)
+	id, _, _, err := c.GetNetworkInAnyState(project, oldName)
 	if err != nil {
 		return err
 	}

From 9b9249fc1edb4231d06b1c8138321e17ca9c5df9 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 23 Nov 2020 17:14:07 +0000
Subject: [PATCH 22/35] lxd/db/networks: Updates comments to reference state
 constants

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

diff --git a/lxd/db/networks.go b/lxd/db/networks.go
index 12866e0344..34a75003a6 100644
--- a/lxd/db/networks.go
+++ b/lxd/db/networks.go
@@ -175,7 +175,7 @@ func (c *ClusterTx) CreateNetworkConfig(networkID, nodeID int64, config map[stri
 // and we just need to track it.
 func (c *ClusterTx) NetworkNodeJoin(networkID, nodeID int64) error {
 	columns := []string{"network_id", "node_id", "state"}
-	// Create network node with "created" state as we expect the network to already be setup.
+	// Create network node with networkCreated state as we expect the network to already be setup.
 	values := []interface{}{networkID, nodeID, networkCreated}
 	_, err := query.UpsertObject(c.tx, "networks_nodes", columns, values)
 	return err
@@ -276,7 +276,7 @@ func (c *ClusterTx) CreatePendingNetwork(node string, projectName string, name s
 			return err
 		}
 	} else {
-		// Check that the existing network is in the pending state.
+		// Check that the existing network is in the networkPending or networkErrored state.
 		if network.state != networkPending && network.state != networkErrored {
 			return fmt.Errorf("Network is not in pending or errored state")
 		}
@@ -302,7 +302,7 @@ func (c *ClusterTx) CreatePendingNetwork(node string, projectName string, name s
 		return ErrAlreadyDefined
 	}
 
-	// Insert the node-specific configuration with state "pending".
+	// Insert the node-specific configuration with state networkPending.
 	columns := []string{"network_id", "node_id", "state"}
 	values := []interface{}{networkID, nodeInfo.ID, networkPending}
 	_, err = query.UpsertObject(c.tx, "networks_nodes", columns, values)
@@ -318,12 +318,12 @@ func (c *ClusterTx) CreatePendingNetwork(node string, projectName string, name s
 	return nil
 }
 
-// NetworkCreated sets the state of the given network to "Created".
+// NetworkCreated sets the state of the given network to 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".
+// NetworkErrored sets the state of the given network to networkErrored.
 func (c *ClusterTx) NetworkErrored(project string, name string) error {
 	return c.networkState(project, name, networkErrored)
 }
@@ -344,7 +344,7 @@ func (c *ClusterTx) networkState(project string, name string, state NetworkState
 	return nil
 }
 
-// NetworkNodeCreated sets the state of the given network for the local member to "Created".
+// NetworkNodeCreated sets the state of the given network for the local member to networkCreated.
 func (c *ClusterTx) NetworkNodeCreated(networkID int64) error {
 	return c.networkNodeState(networkID, networkCreated)
 }
@@ -423,7 +423,7 @@ func (c *Cluster) GetNetworks(project string) ([]string, error) {
 	return c.networks(project, "")
 }
 
-// GetNonPendingNetworks returns the names of all networks that are not pending.
+// GetNonPendingNetworks returns the names of all networks that are not in state networkPending.
 func (c *Cluster) GetNonPendingNetworks(project string) ([]string, error) {
 	return c.networks(project, "NOT state=?", networkPending)
 }
@@ -685,7 +685,7 @@ func (c *Cluster) getNetworkConfig(id int64) (map[string]string, error) {
 func (c *Cluster) CreateNetwork(projectName string, name string, description string, netType NetworkType, config map[string]string) (int64, error) {
 	var id int64
 	err := c.Transaction(func(tx *ClusterTx) error {
-		// Insert a new network record with state "created".
+		// Insert a new network record with state networkCreated.
 		result, err := tx.tx.Exec("INSERT INTO networks (project_id, name, description, state, type) VALUES ((SELECT id FROM projects WHERE name = ?), ?, ?, ?, ?)",
 			projectName, name, description, networkCreated, netType)
 		if err != nil {
@@ -697,7 +697,7 @@ func (c *Cluster) CreateNetwork(projectName string, name string, description str
 			return err
 		}
 
-		// Insert a node-specific entry pointing to ourselves with state "pending".
+		// Insert a node-specific entry pointing to ourselves with state networkPending.
 		columns := []string{"network_id", "node_id", "state"}
 		values := []interface{}{id, c.nodeID, networkPending}
 		_, err = query.UpsertObject(tx.tx, "networks_nodes", columns, values)

From 50cbd073f9eab83122a05a604c6b529767e1b795 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 23 Nov 2020 17:14:44 +0000
Subject: [PATCH 23/35] lxd/patches: d.cluster.GetNetworkInAnyState usage

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

diff --git a/lxd/patches.go b/lxd/patches.go
index 194a5695c9..1e6fa8183c 100644
--- a/lxd/patches.go
+++ b/lxd/patches.go
@@ -3787,7 +3787,7 @@ func patchNetworkCearBridgeVolatileHwaddr(name string, d *Daemon) error {
 	}
 
 	for _, networkName := range networks {
-		_, net, err := d.cluster.GetNetworkInAnyState(projectName, 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)
 		}

From 197c40fc772588292f7e1ce05ef89bd6a790d227 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 23 Nov 2020 17:16:31 +0000
Subject: [PATCH 24/35] lxd/api/cluster: d.cluster.GetNetworkInAnyState usage

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

diff --git a/lxd/api_cluster.go b/lxd/api_cluster.go
index 7e88bcce2f..3167c0d83f 100644
--- a/lxd/api_cluster.go
+++ b/lxd/api_cluster.go
@@ -436,7 +436,7 @@ func clusterPutJoin(d *Daemon, req api.ClusterPut) response.Response {
 			}
 
 			for _, name := range networkNames {
-				_, network, err := d.cluster.GetNetworkInAnyState(p.Name, name)
+				_, network, _, err := d.cluster.GetNetworkInAnyState(p.Name, name)
 				if err != nil {
 					return err
 				}
@@ -1628,7 +1628,7 @@ func clusterCheckNetworksMatch(cluster *db.Cluster, reqNetworks []internalCluste
 
 				found = true
 
-				_, network, err := cluster.GetNetworkInAnyState(networkProjectName, networkName)
+				_, network, _, err := cluster.GetNetworkInAnyState(networkProjectName, networkName)
 				if err != nil {
 					return err
 				}

From c1d5bfed2eb8963255b3f8f9d126270af5f8d7d2 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 23 Nov 2020 17:16:43 +0000
Subject: [PATCH 25/35] lxd/api/project: s.Cluster.GetNetworkInAnyState usage

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

diff --git a/lxd/api_project.go b/lxd/api_project.go
index 85de1f61b2..cbb088cb85 100644
--- a/lxd/api_project.go
+++ b/lxd/api_project.go
@@ -624,7 +624,7 @@ func projectValidateRestrictedSubnets(s *state.State, value string) error {
 		}
 
 		// Check uplink exists and load config to compare subnets.
-		_, uplink, err := s.Cluster.GetNetworkInAnyState(project.Default, uplinkName)
+		_, uplink, _, err := s.Cluster.GetNetworkInAnyState(project.Default, uplinkName)
 		if err != nil {
 			return errors.Wrapf(err, "Invalid uplink network %q", uplinkName)
 		}

From ac5ac8373ccbd3983a58117881900ad086d7a6a1 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 23 Nov 2020 17:16:53 +0000
Subject: [PATCH 26/35] lxd/device/nic: d.state.Cluster.GetNetworkInAnyState
 usage

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/device/nic_bridged.go     | 2 +-
 lxd/device/nictype/nictype.go | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/lxd/device/nic_bridged.go b/lxd/device/nic_bridged.go
index f79fd3549c..83e78ed092 100644
--- a/lxd/device/nic_bridged.go
+++ b/lxd/device/nic_bridged.go
@@ -492,7 +492,7 @@ func (d *nicBridged) rebuildDnsmasqEntry() error {
 	defer dnsmasq.ConfigMutex.Unlock()
 
 	// Use project.Default here as bridge networks don't support projects.
-	_, dbInfo, err := d.state.Cluster.GetNetworkInAnyState(project.Default, d.config["parent"])
+	_, dbInfo, _, err := d.state.Cluster.GetNetworkInAnyState(project.Default, d.config["parent"])
 	if err != nil {
 		return err
 	}
diff --git a/lxd/device/nictype/nictype.go b/lxd/device/nictype/nictype.go
index 5c8ef08209..3848864855 100644
--- a/lxd/device/nictype/nictype.go
+++ b/lxd/device/nictype/nictype.go
@@ -26,7 +26,7 @@ func NICType(s *state.State, deviceProjectName string, d deviceConfig.Device) (s
 				return "", errors.Wrapf(err, "Failed to translate device project %q into network project", deviceProjectName)
 			}
 
-			_, netInfo, err := s.Cluster.GetNetworkInAnyState(networkProjectName, d["network"])
+			_, 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)
 			}

From 9f5cbbed88dc2b13d355cd468a9d021da72a50ed Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 23 Nov 2020 17:18:19 +0000
Subject: [PATCH 27/35] lxd/network/driver/ovn:
 n.state.Cluster.GetNetworkInAnyState usage

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

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index 097aaeb011..6c855330b3 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -82,7 +82,7 @@ func (n *ovn) Info() Info {
 
 // uplinkRoutes parses ipv4.routes and ipv6.routes settings for a named uplink network into a slice of *net.IPNet.
 func (n *ovn) uplinkRoutes(uplinkNetworkName string) ([]*net.IPNet, error) {
-	_, uplink, err := n.state.Cluster.GetNetworkInAnyState(project.Default, uplinkNetworkName)
+	_, uplink, _, err := n.state.Cluster.GetNetworkInAnyState(project.Default, uplinkNetworkName)
 	if err != nil {
 		return nil, err
 	}

From 1b2c63d4f1c3e750acb692744736f2f55e04f06e Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 23 Nov 2020 17:19:29 +0000
Subject: [PATCH 28/35] lxd/network/driver/common: Adds LocalStatus function
 and store node info inside network via init()

Updates init() to accept api.Network and map of NetworkNodes.

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

diff --git a/lxd/network/driver_common.go b/lxd/network/driver_common.go
index 23cff9a58c..6c39a6d7b2 100644
--- a/lxd/network/driver_common.go
+++ b/lxd/network/driver_common.go
@@ -36,18 +36,20 @@ type common struct {
 	description string
 	config      map[string]string
 	status      string
+	nodes       map[int64]db.NetworkNode
 }
 
 // init initialise internal variables.
-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})
+func (n *common) init(state *state.State, id int64, projectName string, netInfo *api.Network, netNodes map[int64]db.NetworkNode) {
+	n.logger = logging.AddContext(logger.Log, log.Ctx{"project": projectName, "driver": netInfo.Type, "network": netInfo.Name})
 	n.id = id
 	n.project = projectName
-	n.name = name
-	n.config = config
+	n.name = netInfo.Name
+	n.config = netInfo.Config
 	n.state = state
-	n.description = description
-	n.status = status
+	n.description = netInfo.Description
+	n.status = netInfo.Status
+	n.nodes = netNodes
 }
 
 // FillConfig fills requested config with any default values, by default this is a no-op.
@@ -133,6 +135,16 @@ func (n *common) Status() string {
 	return n.status
 }
 
+// LocalStatus returns network status of the local cluster member.
+func (n *common) LocalStatus() string {
+	node, exists := n.nodes[n.state.Cluster.GetNodeID()]
+	if !exists {
+		return api.NetworkStatusUnknown
+	}
+
+	return db.NetworkStateToAPIStatus(node.State)
+}
+
 // Config returns the network config.
 func (n *common) Config() map[string]string {
 	return n.config

From 0768ddc46d083fa36b9fb405b8d3c90ae9c7d671 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 23 Nov 2020 17:17:32 +0000
Subject: [PATCH 29/35] lxd/network/driver/bridge: Only perform local date if
 local status is api.NetworkStatusCreated

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

diff --git a/lxd/network/driver_bridge.go b/lxd/network/driver_bridge.go
index 8014bdb256..0175e930e3 100644
--- a/lxd/network/driver_bridge.go
+++ b/lxd/network/driver_bridge.go
@@ -414,20 +414,22 @@ func (n *bridge) isRunning() bool {
 func (n *bridge) Delete(clientType cluster.ClientType) error {
 	n.logger.Debug("Delete", log.Ctx{"clientType": clientType})
 
-	// Bring the network down.
-	if n.isRunning() {
-		err := n.Stop()
+	// Bring the local network down if created on this node.
+	if n.LocalStatus() == api.NetworkStatusCreated {
+		if n.isRunning() {
+			err := n.Stop()
+			if err != nil {
+				return err
+			}
+		}
+
+		// Delete apparmor profiles.
+		err := apparmor.NetworkDelete(n.state, n)
 		if err != nil {
 			return err
 		}
 	}
 
-	// Delete apparmor profiles.
-	err := apparmor.NetworkDelete(n.state, n)
-	if err != nil {
-		return err
-	}
-
 	return n.common.delete(clientType)
 }
 

From 62ae92fb4d2863690b441f0a28a07c02b17c6d9a Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 23 Nov 2020 17:18:11 +0000
Subject: [PATCH 30/35] lxd/network/driver/ovn: Only perform local date if
 local status is api.NetworkStatusCreated

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

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index 6c855330b3..370104179c 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -1758,61 +1758,63 @@ func (n *ovn) deleteChassisGroupEntry() error {
 func (n *ovn) Delete(clientType cluster.ClientType) error {
 	n.logger.Debug("Delete", log.Ctx{"clientType": clientType})
 
-	err := n.Stop()
-	if err != nil {
-		return err
-	}
-
-	if clientType == cluster.ClientTypeNormal {
-		client, err := n.getClient()
+	if n.LocalStatus() == api.NetworkStatusCreated {
+		err := n.Stop()
 		if err != nil {
 			return err
 		}
 
-		err = client.LogicalRouterDelete(n.getRouterName())
-		if err != nil {
-			return err
-		}
+		if clientType == cluster.ClientTypeNormal {
+			client, err := n.getClient()
+			if err != nil {
+				return err
+			}
 
-		err = client.LogicalSwitchDelete(n.getExtSwitchName())
-		if err != nil {
-			return err
-		}
+			err = client.LogicalRouterDelete(n.getRouterName())
+			if err != nil {
+				return err
+			}
 
-		err = client.LogicalSwitchDelete(n.getIntSwitchName())
-		if err != nil {
-			return err
-		}
+			err = client.LogicalSwitchDelete(n.getExtSwitchName())
+			if err != nil {
+				return err
+			}
 
-		err = client.LogicalRouterPortDelete(n.getRouterExtPortName())
-		if err != nil {
-			return err
-		}
+			err = client.LogicalSwitchDelete(n.getIntSwitchName())
+			if err != nil {
+				return err
+			}
 
-		err = client.LogicalRouterPortDelete(n.getRouterIntPortName())
-		if err != nil {
-			return err
-		}
+			err = client.LogicalRouterPortDelete(n.getRouterExtPortName())
+			if err != nil {
+				return err
+			}
 
-		err = client.LogicalSwitchPortDelete(n.getExtSwitchRouterPortName())
-		if err != nil {
-			return err
-		}
+			err = client.LogicalRouterPortDelete(n.getRouterIntPortName())
+			if err != nil {
+				return err
+			}
 
-		err = client.LogicalSwitchPortDelete(n.getExtSwitchProviderPortName())
-		if err != nil {
-			return err
-		}
+			err = client.LogicalSwitchPortDelete(n.getExtSwitchRouterPortName())
+			if err != nil {
+				return err
+			}
 
-		err = client.LogicalSwitchPortDelete(n.getIntSwitchRouterPortName())
-		if err != nil {
-			return err
-		}
+			err = client.LogicalSwitchPortDelete(n.getExtSwitchProviderPortName())
+			if err != nil {
+				return err
+			}
 
-		// Must be done after logical router removal.
-		err = client.ChassisGroupDelete(n.getChassisGroupName())
-		if err != nil {
-			return err
+			err = client.LogicalSwitchPortDelete(n.getIntSwitchRouterPortName())
+			if err != nil {
+				return err
+			}
+
+			// Must be done after logical router removal.
+			err = client.ChassisGroupDelete(n.getChassisGroupName())
+			if err != nil {
+				return err
+			}
 		}
 	}
 

From 272c6e74cffdcbd54c91a3f6e05c4f7d6467d69e Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 23 Nov 2020 17:20:08 +0000
Subject: [PATCH 31/35] lxd/network/driver/physical: Only perform local date if
 local status is api.NetworkStatusCreated

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

diff --git a/lxd/network/driver_physical.go b/lxd/network/driver_physical.go
index 4ee105c8f2..09d94f59a4 100644
--- a/lxd/network/driver_physical.go
+++ b/lxd/network/driver_physical.go
@@ -118,9 +118,11 @@ func (n *physical) Create(clientType cluster.ClientType) error {
 func (n *physical) Delete(clientType cluster.ClientType) error {
 	n.logger.Debug("Delete", log.Ctx{"clientType": clientType})
 
-	err := n.Stop()
-	if err != nil {
-		return err
+	if n.LocalStatus() == api.NetworkStatusCreated {
+		err := n.Stop()
+		if err != nil {
+			return err
+		}
 	}
 
 	return n.common.delete(clientType)

From 9882e5f2d3a01674be19ed5075640ac82e0b983a Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 23 Nov 2020 17:24:48 +0000
Subject: [PATCH 32/35] lxd/networks: Updates doNetworksCreate to skip creation
 if node is already marked created

Updates node status to created on successful create.

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

diff --git a/lxd/networks.go b/lxd/networks.go
index a4fec44216..6f3c1817d1 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -444,6 +444,15 @@ func doNetworksCreate(d *Daemon, projectName string, req api.NetworksPost, clien
 		return err
 	}
 
+	if n.Status() == api.NetworkStatusCreated {
+		return fmt.Errorf("Network already created")
+	}
+
+	if n.LocalStatus() == api.NetworkStatusCreated {
+		logger.Debug("Skipping network create as already created locally", log.Ctx{"project": projectName, "network": n.Name()})
+		return nil
+	}
+
 	// Run initial creation setup for the network driver.
 	err = n.Create(clientType)
 	if err != nil {
@@ -457,13 +466,26 @@ func doNetworksCreate(d *Daemon, projectName string, req api.NetworksPost, clien
 		if err != nil {
 			delErr := n.Delete(clientType)
 			if delErr != nil {
-				logger.Errorf("Failed clearing up network %q after failed create: %v", n.Name(), delErr)
+				logger.Error("Failed clearing up network after failed create", log.Ctx{"project": projectName, "network": n.Name(), "err": delErr})
 			}
 
 			return err
 		}
 	}
 
+	// Mark local node as state "created".
+	err = d.cluster.Transaction(func(tx *db.ClusterTx) error {
+		return tx.NetworkNodeCreated(n.ID())
+	})
+	if err != nil {
+		delErr := n.Delete(clientType)
+		if delErr != nil {
+			logger.Error("Failed clearing up network after failed local status update", log.Ctx{"project": projectName, "network": n.Name(), "err": delErr})
+		}
+		return err
+	}
+	logger.Debug("Marked network local status as created", log.Ctx{"project": projectName, "network": req.Name})
+
 	return nil
 }
 

From d4181329e595c2c58ce857572d4d08576f507404 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 23 Nov 2020 17:25:32 +0000
Subject: [PATCH 33/35] lxd/networks: d.cluster.GetNetworkInAnyState usage

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 6f3c1817d1..e19d11b9ae 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -533,7 +533,7 @@ func doNetworkGet(d *Daemon, projectName string, name string) (api.Network, erro
 	}
 
 	// Get some information.
-	_, dbInfo, _ := d.cluster.GetNetworkInAnyState(projectName, name)
+	_, dbInfo, _, _ := d.cluster.GetNetworkInAnyState(projectName, name)
 
 	// Don't allow retrieving info about the local node interfaces when not using default project.
 	if projectName != project.Default && dbInfo == nil {

From 1dde572bb720b2b25745e3f49681ab0f81f12baa Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 23 Nov 2020 17:26:00 +0000
Subject: [PATCH 34/35] lxd/networks: Don't skip network clean up if network is
 pending in networkDelete()

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

diff --git a/lxd/networks.go b/lxd/networks.go
index e19d11b9ae..be10a2ade4 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -605,19 +605,6 @@ func networkDelete(d *Daemon, r *http.Request) response.Response {
 	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(projectName, name)
-	if err != nil {
-		return response.SmartError(err)
-	}
-	if dbNetwork.Status == api.NetworkStatusPending {
-		err := d.cluster.DeleteNetwork(projectName, name)
-		if err != nil {
-			return response.SmartError(err)
-		}
-		return response.EmptySyncResponse
-	}
-
 	// Get the existing network.
 	n, err := network.LoadByName(state, projectName, name)
 	if err != nil {
@@ -639,7 +626,7 @@ func networkDelete(d *Daemon, r *http.Request) response.Response {
 		}
 	}
 
-	// Delete the network.
+	// Delete the network from each member.
 	err = n.Delete(clientType)
 	if err != nil {
 		return response.SmartError(err)

From 2623f054c30aa8dc759afa8629fe5a24c8c5a60f Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 23 Nov 2020 17:26:34 +0000
Subject: [PATCH 35/35] lxd/networks: d.cluster.GetNetworkInAnyState usage

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 be10a2ade4..9fa0b799b0 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -322,7 +322,7 @@ func networksPostCluster(d *Daemon, projectName string, req api.NetworksPost, cl
 
 	// 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(projectName, req.Name)
+	_, netInfo, _, err := d.cluster.GetNetworkInAnyState(projectName, req.Name)
 	if err != nil && err != db.ErrNoSuchObject {
 		return err
 	}
@@ -730,7 +730,7 @@ func networkPut(d *Daemon, r *http.Request) response.Response {
 	name := mux.Vars(r)["name"]
 
 	// Get the existing network.
-	_, dbInfo, err := d.cluster.GetNetworkInAnyState(projectName, name)
+	_, dbInfo, _, err := d.cluster.GetNetworkInAnyState(projectName, name)
 	if err != nil {
 		return response.SmartError(err)
 	}


More information about the lxc-devel mailing list