The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/8180
This e-mail was sent by the LXC bot, direct replies will not reach the author unless they happen to be subscribed to this list. === Description (from pull-request) === - Adds state column to networks_nodes table. - Modifies network create process to allow per-node state tracking. - Allows fixing of per-node creation blockers when creating a network in a cluster. Related to https://github.com/lxc/lxd/issues/8111
From 94f106b017fbb3c22805898981cbeace5d67b4fe Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Fri, 20 Nov 2020 11:44:34 +0000 Subject: [PATCH 01/37] lxd/storage/pools/utils: Updates comment and error for storagePoolCreateLocal Makes more accurate. Signed-off-by: Thomas Parrott <thomas.parr...@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 ecc1cdb3022219c536aba6b02b2191e0fcf059a5 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Fri, 20 Nov 2020 11:46:21 +0000 Subject: [PATCH 02/37] lxd/storage/pools: Error quoting Signed-off-by: Thomas Parrott <thomas.parr...@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 43d0cba9443e07e7f828b9495e94ae5684db4e7a Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Fri, 20 Nov 2020 14:37:47 +0000 Subject: [PATCH 03/37] 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.parr...@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 20283679c22da495d2090fa7806c193a49016c31 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 23 Nov 2020 11:17:12 +0000 Subject: [PATCH 04/37] lxd/db/networks: Populate node state column in NetworkNodeJoin Signed-off-by: Thomas Parrott <thomas.parr...@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 cd243854421b89c79611273020235a28139c49bf Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 23 Nov 2020 11:17:36 +0000 Subject: [PATCH 05/37] lxd/db/networks: Populate node state column in CreatePendingNetwork Signed-off-by: Thomas Parrott <thomas.parr...@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 726620ad3e975a8c458914effb3361ef8f1eedfb Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 23 Nov 2020 11:18:01 +0000 Subject: [PATCH 06/37] lxd/db/networks: Adds networkNodeState and NetworkNodeCreated functiond Signed-off-by: Thomas Parrott <thomas.parr...@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 0973a46ecabb4d5a83612624940e0f2fde9852f6 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 23 Nov 2020 11:18:37 +0000 Subject: [PATCH 07/37] lxd/db/networks: Comments Signed-off-by: Thomas Parrott <thomas.parr...@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 e8c987b6ffea5e0c2296701ae71e4b6fd28b32b6 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 23 Nov 2020 11:18:49 +0000 Subject: [PATCH 08/37] lxd/db/networks: Populate node state column in CreateNetwork Signed-off-by: Thomas Parrott <thomas.parr...@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 c9d06cbfc278966704a801cea8016858d9e6a537 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 23 Nov 2020 11:19:31 +0000 Subject: [PATCH 09/37] 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.parr...@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 cc6f8548c20af0bb9112ec1daab8ee04080206f0 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 23 Nov 2020 11:20:38 +0000 Subject: [PATCH 10/37] lxd/networks: Whitespace Signed-off-by: Thomas Parrott <thomas.parr...@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 8fe254d8c90b9b8cabc58bd88572e07dabf83ce4 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 23 Nov 2020 17:02:25 +0000 Subject: [PATCH 11/37] lxd/network/network/interface: Updates init to take api.Network and network nodes map Signed-off-by: Thomas Parrott <thomas.parr...@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 1bf2a21a6d885274a1a173d91cad1d924814a0c1 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 23 Nov 2020 17:02:57 +0000 Subject: [PATCH 12/37] lxd/network/network/interface: Adds LocalStatus Signed-off-by: Thomas Parrott <thomas.parr...@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 3e8161904004bc8af61cd70e9c330b3423c9e584 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 23 Nov 2020 17:03:26 +0000 Subject: [PATCH 13/37] lxd/network/network/load: Updates LoadByName to pass network nodes from s.Cluster.GetNetworkInAnyState to init() Signed-off-by: Thomas Parrott <thomas.parr...@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 2a10a295e9dad218cc0ab0a0defc21aab48fd771 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 23 Nov 2020 17:05:56 +0000 Subject: [PATCH 14/37] lxd/db/networks: Adds NetworkState type and uses it in place of int Signed-off-by: Thomas Parrott <thomas.parr...@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 10c53771be5eace214e35282964b81f43a821b2b Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 23 Nov 2020 17:07:37 +0000 Subject: [PATCH 15/37] lxd/db/networks: Renames networkFillStatus to NetworkStateToAPIStatus And updates usage. Signed-off-by: Thomas Parrott <thomas.parr...@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 31ace3f2ed6d2c3860a058fb992c4ff12d398635 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 23 Nov 2020 17:09:45 +0000 Subject: [PATCH 16/37] lxd/db/networks: Adds NetworkNode type Signed-off-by: Thomas Parrott <thomas.parr...@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 52140c740a8e3f4ce5ba59a03e0bdae92e57012f Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 23 Nov 2020 17:10:11 +0000 Subject: [PATCH 17/37] lxd/db/networks: Exports NetworkNodes and updates to return map of NetworkNodes Including node ID, name and state. Signed-off-by: Thomas Parrott <thomas.parr...@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 bc3108c16910af7a46274e50b52a0734afd673f6 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 23 Nov 2020 17:11:41 +0000 Subject: [PATCH 18/37] lxd/db/networks: Updates GetNonPendingNetworks usage of NetworkNodes() Signed-off-by: Thomas Parrott <thomas.parr...@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 a0f20b9a19926fec4033572c6d4af887f4423650 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 23 Nov 2020 17:12:10 +0000 Subject: [PATCH 19/37] 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.parr...@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 36f25d841d4245cf5ab37dcc4ca7d9e019e79647 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 23 Nov 2020 17:13:07 +0000 Subject: [PATCH 20/37] lxd/db/networks: Exports NetworkNodes Signed-off-by: Thomas Parrott <thomas.parr...@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 0daa38e09bff9b01b6725586229828355766803a Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 23 Nov 2020 17:13:49 +0000 Subject: [PATCH 21/37] lxd/db/networks: c.GetNetworkInAnyState usage Signed-off-by: Thomas Parrott <thomas.parr...@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 b914b831e65e35ed3ce8b81dbbff14accf10bf3f Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 23 Nov 2020 17:14:07 +0000 Subject: [PATCH 22/37] lxd/db/networks: Updates comments to reference state constants Signed-off-by: Thomas Parrott <thomas.parr...@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 c0ce479baf45772ff367e66892a50238c3a1b38c Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 23 Nov 2020 17:14:44 +0000 Subject: [PATCH 23/37] lxd/patches: d.cluster.GetNetworkInAnyState usage Signed-off-by: Thomas Parrott <thomas.parr...@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 a059aee307388e5c18ecc3b1a82fa8d268064a84 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 23 Nov 2020 17:16:31 +0000 Subject: [PATCH 24/37] lxd/api/cluster: d.cluster.GetNetworkInAnyState usage Signed-off-by: Thomas Parrott <thomas.parr...@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 55e5ce01af334a72e9784bd37ceff3cd4f3774cf Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 23 Nov 2020 17:16:43 +0000 Subject: [PATCH 25/37] lxd/api/project: s.Cluster.GetNetworkInAnyState usage Signed-off-by: Thomas Parrott <thomas.parr...@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 e89c92f2f969ff910edda33b2a8d12d6427c94aa Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 23 Nov 2020 17:16:53 +0000 Subject: [PATCH 26/37] lxd/device/nic: d.state.Cluster.GetNetworkInAnyState usage Signed-off-by: Thomas Parrott <thomas.parr...@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 5223624dea787970919240a11791347ba49d6787 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 23 Nov 2020 17:18:19 +0000 Subject: [PATCH 27/37] lxd/network/driver/ovn: n.state.Cluster.GetNetworkInAnyState usage Signed-off-by: Thomas Parrott <thomas.parr...@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 9079c21409a0f4b361682fbb8e8fcab60f38817e Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 23 Nov 2020 17:19:29 +0000 Subject: [PATCH 28/37] 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.parr...@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 1fed7bc2c9f5142c53910a14b94b8eea8d0bd41f Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 23 Nov 2020 17:17:32 +0000 Subject: [PATCH 29/37] lxd/network/driver/bridge: Only perform local date if local status is api.NetworkStatusCreated Signed-off-by: Thomas Parrott <thomas.parr...@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 d0b90906fdb44bc612bd739eeb7deb79da51a552 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 23 Nov 2020 17:18:11 +0000 Subject: [PATCH 30/37] lxd/network/driver/ovn: Only perform local date if local status is api.NetworkStatusCreated Signed-off-by: Thomas Parrott <thomas.parr...@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 3aa894b481e1c14fe778a5964eea4e17c123c5f5 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 23 Nov 2020 17:20:08 +0000 Subject: [PATCH 31/37] lxd/network/driver/physical: Only perform local date if local status is api.NetworkStatusCreated Signed-off-by: Thomas Parrott <thomas.parr...@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 a8e123c0337bb67fa9f5fc9a4ccf618ecbfe1f79 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 23 Nov 2020 17:24:48 +0000 Subject: [PATCH 32/37] 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.parr...@canonical.com> --- lxd/networks.go | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/lxd/networks.go b/lxd/networks.go index a4fec44216..4a1205edca 100644 --- a/lxd/networks.go +++ b/lxd/networks.go @@ -444,6 +444,11 @@ func doNetworksCreate(d *Daemon, projectName string, req api.NetworksPost, clien return err } + 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 +462,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 as status as networkCreated. + 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 0338b87c317db87c77e0ce0c82c68f7a20f9327a Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 23 Nov 2020 17:25:32 +0000 Subject: [PATCH 33/37] lxd/networks: d.cluster.GetNetworkInAnyState usage Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/networks.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/networks.go b/lxd/networks.go index 4a1205edca..866d6e4125 100644 --- a/lxd/networks.go +++ b/lxd/networks.go @@ -529,7 +529,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 7863bf7fee38f547e2f32f552fa2502c22819fb0 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 23 Nov 2020 17:26:00 +0000 Subject: [PATCH 34/37] lxd/networks: Don't skip network clean up if network is pending in networkDelete() Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/networks.go | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/lxd/networks.go b/lxd/networks.go index 866d6e4125..fd8a82ef35 100644 --- a/lxd/networks.go +++ b/lxd/networks.go @@ -601,19 +601,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 { @@ -635,7 +622,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 5e551c15a2533b0f2db5c6e7cb5b879e87f26b4e Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 23 Nov 2020 17:26:34 +0000 Subject: [PATCH 35/37] lxd/networks: d.cluster.GetNetworkInAnyState usage Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/networks.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lxd/networks.go b/lxd/networks.go index fd8a82ef35..6207073172 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 } @@ -726,7 +726,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) } From 421d5d40291ba03bddf5f87157c6201e1b1e5116 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 23 Nov 2020 17:41:56 +0000 Subject: [PATCH 36/37] lxd/networks: Updates networksPostCluster to only mark global network states as created once all nodes created Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/networks.go | 52 ++++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/lxd/networks.go b/lxd/networks.go index 6207073172..4896f116f8 100644 --- a/lxd/networks.go +++ b/lxd/networks.go @@ -312,6 +312,8 @@ func networksPost(d *Daemon, r *http.Request) response.Response { return resp } +// networksPostCluster checks that there is a pending network in the database and then attempts to setup the +// network on each node. If all nodes are successfully setup then the network's state is set to created. func networksPostCluster(d *Daemon, projectName string, req api.NetworksPost, clientType cluster.ClientType, netType network.Type) error { // Check that no node-specific config key has been supplied in request. for key := range req.Config { @@ -371,43 +373,27 @@ func networksPostCluster(d *Daemon, projectName string, req api.NetworksPost, cl return err } - // Create the network on this node. - nodeReq := req - - // Merge node specific config items into global config. - for key, value := range configs[nodeName] { - nodeReq.Config[key] = value - } - - // Notify all other nodes to create the network. + // Create notifier for other nodes to create the network. notifier, err := cluster.NewNotifier(d.State(), d.endpoints.NetworkCert(), cluster.NotifyAll) if err != nil { return err } - revert := revert.New() - defer revert.Fail() - - revert.Add(func() { - d.cluster.Transaction(func(tx *db.ClusterTx) error { - return tx.NetworkErrored(projectName, req.Name) - }) - }) + // Create the network on this node. + nodeReq := req - // 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(projectName, req.Name) - }) - if err != nil { - return err + // Merge node specific config items into global config. + for key, value := range configs[nodeName] { + nodeReq.Config[key] = value } err = doNetworksCreate(d, projectName, nodeReq, clientType) if err != nil { return err } + logger.Error("Created network on local cluster member", log.Ctx{"project": projectName, "network": req.Name}) + // Notify other nodes to create the network. err = notifier(func(client lxd.InstanceServer) error { server, _, err := client.GetServer() if err != nil { @@ -419,13 +405,27 @@ func networksPostCluster(d *Daemon, projectName string, req api.NetworksPost, cl nodeReq.Config[key] = value } - return client.UseProject(projectName).CreateNetwork(nodeReq) + err = client.UseProject(projectName).CreateNetwork(nodeReq) + if err != nil { + return err + } + logger.Error("Created network on cluster member", log.Ctx{"project": projectName, "network": req.Name, "member": server.Environment.ServerName}) + + return nil }) if err != nil { return err } - revert.Success() + // Mark network global status as networkCreated now that all nodes have succeeded. + err = d.cluster.Transaction(func(tx *db.ClusterTx) error { + return tx.NetworkCreated(projectName, req.Name) + }) + if err != nil { + return err + } + logger.Debug("Marked network global status as created", log.Ctx{"project": projectName, "network": req.Name}) + return nil } From 10fe44e00eb6bfbc72f582dfaac80f42069f593f Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 23 Nov 2020 17:51:11 +0000 Subject: [PATCH 37/37] lxd/db/migration/test: cluster.GetNetworkInAnyState usage Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/db/migration_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/db/migration_test.go b/lxd/db/migration_test.go index fb2c07d16c..1986c355c2 100644 --- a/lxd/db/migration_test.go +++ b/lxd/db/migration_test.go @@ -90,7 +90,7 @@ func TestImportPreClusteringData(t *testing.T) { networks, err := cluster.GetNetworks(project.Default) require.NoError(t, err) assert.Equal(t, []string{"lxcbr0"}, networks) - id, network, err := cluster.GetNetworkInAnyState(project.Default, "lxcbr0") + id, network, _, err := cluster.GetNetworkInAnyState(project.Default, "lxcbr0") require.NoError(t, err) assert.Equal(t, int64(1), id) assert.Equal(t, "true", network.Config["ipv4.nat"])
_______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel