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

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) ===
When a node joins a cluster that has an OVN network, the join process requires that the local storage pools and networks are created on the joining node (both in the local database and any host-side setup, such as creating network interfaces).

For networks this is achieved by calling the `n.Create()` followed by the `n.Start()` functions on the network driver.
However for OVN networks this causes a problem because whilst the `n.Start()` function is expected to be called on every node, the `n.Create()` function creates the logical network configuration in the remote OVN cluster's northbound database.

As we are joining an existing cluster it is expected that this logical configuration already exists in the OVN northbound database and such the creation will fail with resource conflicts.

We need a way to tell LXD in the network create request prior to completing the cluster join process that we want it to populate its local database and perform any host-local system setup, but avoid doing anything that modifies an existing central remote system (in this case the OVN northbound database).

There is already precedent for using the HTTP client's user agent field to indicate cluster specific activities, as the `lxd-cluster-notifier` user agent is used when creating resources (such as networks) via the API when a node is already in a cluster and we just want it to complete any node-local setup without modifying the LXD database.

This PR extends this concept by adding another cluster specific user agent `lxd-cluster-joiner` which is sent by the LXD when it connects back to itself to complete pre-join local node setup.

This is then used by the OVN network driver to detect a pre-join is in process and avoids modifying the OVN northbound database during the creation process.

By using an additional HTTP user agent this allows us to reuse the network create API routers (as they are today) and indicate to the network driver a join is in process without needing to modify the published API structures.

It turns out that ceph storage pools also have need for this same concept (which is currently handled outside of the driver as an exception). This PR lays the groundwork for the ceph storage pool driver to use the same concept to alter its storage pool create behaviour to not try and create the pool on the remote ceph cluster (which will follow in a separate PR).
From 4a1c3e61f862aef59c59baaae9f63faee5f4d176 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 26 Aug 2020 14:29:42 +0100
Subject: [PATCH 01/15] lxd/network/driver/ovn: Makes ping test in
 startParentPortBridge async

No need to delay LXD startup.

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

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index 1c6d914674..29174efff7 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -591,7 +591,7 @@ func (n *ovn) startParentPortBridge(parentNet Network) 
error {
                // wouldn't work until the next router advertisement was sent 
(which could be several minutes).
                // By pinging the OVN router's external IP this will trigger an 
NDP request from the parent bridge
                // which will cause the OVN router to learn its MAC address.
-               func() {
+               go func() {
                        // Try several attempts as it can take a few seconds 
for the network to come up.
                        for i := 0; i < 5; i++ {
                                if pingIP(routerExtPortIPv6) {
@@ -602,7 +602,9 @@ func (n *ovn) startParentPortBridge(parentNet Network) 
error {
                                time.Sleep(time.Second)
                        }
 
-                       n.logger.Warn("OVN router external IPv6 address 
unreachable", log.Ctx{"ip": routerExtPortIPv6.String()})
+                       // We would expect this on a chassis node that isn't 
the active router gateway, it doesn't
+                       // always indicate a problem.
+                       n.logger.Debug("OVN router external IPv6 address 
unreachable", log.Ctx{"ip": routerExtPortIPv6.String()})
                }()
        }
 

From 8313ca2000d1b28eab17a06ed7e4fa1e2483e67f Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 26 Aug 2020 11:41:20 +0100
Subject: [PATCH 02/15] lxd/init: Updates initDataNodeApply to use revert
 package and to revert itself on error

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/init.go | 74 +++++++++++++++++++----------------------------------
 1 file changed, 27 insertions(+), 47 deletions(-)

diff --git a/lxd/init.go b/lxd/init.go
index ba961a5b85..ca599d459d 100644
--- a/lxd/init.go
+++ b/lxd/init.go
@@ -3,10 +3,12 @@ package main
 import (
        "fmt"
 
+       "github.com/pkg/errors"
+
        lxd "github.com/lxc/lxd/client"
+       "github.com/lxc/lxd/lxd/revert"
        "github.com/lxc/lxd/shared"
        "github.com/lxc/lxd/shared/api"
-       "github.com/pkg/errors"
 )
 
 type initDataNode struct {
@@ -26,34 +28,26 @@ type initDataCluster struct {
 // It's used both by the 'lxd init' command and by the PUT /1.0/cluster API.
 //
 // In case of error, the returned function can be used to revert the changes.
-func initDataNodeApply(d lxd.InstanceServer, config initDataNode) (func(), 
error) {
-       // Handle reverts
-       reverts := []func(){}
-       revert := func() {
-               // Lets undo things in reverse order
-               for i := len(reverts) - 1; i >= 0; i-- {
-                       reverts[i]()
-               }
-       }
+func initDataNodeApply(d lxd.InstanceServer, config initDataNode) error {
+       revert := revert.New()
+       defer revert.Fail()
 
        // Apply server configuration
        if config.Config != nil && len(config.Config) > 0 {
                // Get current config
                currentServer, etag, err := d.GetServer()
                if err != nil {
-                       return revert, errors.Wrap(err, "Failed to retrieve 
current server configuration")
+                       return errors.Wrap(err, "Failed to retrieve current 
server configuration")
                }
 
                // Setup reverter
-               reverts = append(reverts, func() {
-                       d.UpdateServer(currentServer.Writable(), "")
-               })
+               revert.Add(func() { d.UpdateServer(currentServer.Writable(), 
"") })
 
                // Prepare the update
                newServer := api.ServerPut{}
                err = shared.DeepCopy(currentServer.Writable(), &newServer)
                if err != nil {
-                       return revert, errors.Wrap(err, "Failed to copy server 
configuration")
+                       return errors.Wrap(err, "Failed to copy server 
configuration")
                }
 
                for k, v := range config.Config {
@@ -63,7 +57,7 @@ func initDataNodeApply(d lxd.InstanceServer, config 
initDataNode) (func(), error
                // Apply it
                err = d.UpdateServer(newServer, etag)
                if err != nil {
-                       return revert, errors.Wrap(err, "Failed to update 
server configuration")
+                       return errors.Wrap(err, "Failed to update server 
configuration")
                }
        }
 
@@ -72,7 +66,7 @@ func initDataNodeApply(d lxd.InstanceServer, config 
initDataNode) (func(), error
                // Get the list of networks
                networkNames, err := d.GetNetworkNames()
                if err != nil {
-                       return revert, errors.Wrap(err, "Failed to retrieve 
list of networks")
+                       return errors.Wrap(err, "Failed to retrieve list of 
networks")
                }
 
                // Network creator
@@ -84,10 +78,7 @@ func initDataNodeApply(d lxd.InstanceServer, config 
initDataNode) (func(), error
                        }
 
                        // Setup reverter
-                       reverts = append(reverts, func() {
-                               d.DeleteNetwork(network.Name)
-                       })
-
+                       revert.Add(func() { d.DeleteNetwork(network.Name) })
                        return nil
                }
 
@@ -100,9 +91,7 @@ func initDataNodeApply(d lxd.InstanceServer, config 
initDataNode) (func(), error
                        }
 
                        // Setup reverter
-                       reverts = append(reverts, func() {
-                               d.UpdateNetwork(currentNetwork.Name, 
currentNetwork.Writable(), "")
-                       })
+                       revert.Add(func() { 
d.UpdateNetwork(currentNetwork.Name, currentNetwork.Writable(), "") })
 
                        // Prepare the update
                        newNetwork := api.NetworkPut{}
@@ -135,7 +124,7 @@ func initDataNodeApply(d lxd.InstanceServer, config 
initDataNode) (func(), error
                        if !shared.StringInSlice(network.Name, networkNames) {
                                err := createNetwork(network)
                                if err != nil {
-                                       return revert, err
+                                       return err
                                }
 
                                continue
@@ -144,7 +133,7 @@ func initDataNodeApply(d lxd.InstanceServer, config 
initDataNode) (func(), error
                        // Existing network
                        err := updateNetwork(network)
                        if err != nil {
-                               return revert, err
+                               return err
                        }
                }
        }
@@ -154,7 +143,7 @@ func initDataNodeApply(d lxd.InstanceServer, config 
initDataNode) (func(), error
                // Get the list of storagePools
                storagePoolNames, err := d.GetStoragePoolNames()
                if err != nil {
-                       return revert, errors.Wrap(err, "Failed to retrieve 
list of storage pools")
+                       return errors.Wrap(err, "Failed to retrieve list of 
storage pools")
                }
 
                // StoragePool creator
@@ -166,10 +155,7 @@ func initDataNodeApply(d lxd.InstanceServer, config 
initDataNode) (func(), error
                        }
 
                        // Setup reverter
-                       reverts = append(reverts, func() {
-                               d.DeleteStoragePool(storagePool.Name)
-                       })
-
+                       revert.Add(func() { 
d.DeleteStoragePool(storagePool.Name) })
                        return nil
                }
 
@@ -187,9 +173,7 @@ func initDataNodeApply(d lxd.InstanceServer, config 
initDataNode) (func(), error
                        }
 
                        // Setup reverter
-                       reverts = append(reverts, func() {
-                               d.UpdateStoragePool(currentStoragePool.Name, 
currentStoragePool.Writable(), "")
-                       })
+                       revert.Add(func() { 
d.UpdateStoragePool(currentStoragePool.Name, currentStoragePool.Writable(), "") 
})
 
                        // Prepare the update
                        newStoragePool := api.StoragePoolPut{}
@@ -222,7 +206,7 @@ func initDataNodeApply(d lxd.InstanceServer, config 
initDataNode) (func(), error
                        if !shared.StringInSlice(storagePool.Name, 
storagePoolNames) {
                                err := createStoragePool(storagePool)
                                if err != nil {
-                                       return revert, err
+                                       return err
                                }
 
                                continue
@@ -231,7 +215,7 @@ func initDataNodeApply(d lxd.InstanceServer, config 
initDataNode) (func(), error
                        // Existing storagePool
                        err := updateStoragePool(storagePool)
                        if err != nil {
-                               return revert, err
+                               return err
                        }
                }
        }
@@ -241,7 +225,7 @@ func initDataNodeApply(d lxd.InstanceServer, config 
initDataNode) (func(), error
                // Get the list of profiles
                profileNames, err := d.GetProfileNames()
                if err != nil {
-                       return revert, errors.Wrap(err, "Failed to retrieve 
list of profiles")
+                       return errors.Wrap(err, "Failed to retrieve list of 
profiles")
                }
 
                // Profile creator
@@ -253,10 +237,7 @@ func initDataNodeApply(d lxd.InstanceServer, config 
initDataNode) (func(), error
                        }
 
                        // Setup reverter
-                       reverts = append(reverts, func() {
-                               d.DeleteProfile(profile.Name)
-                       })
-
+                       revert.Add(func() { d.DeleteProfile(profile.Name) })
                        return nil
                }
 
@@ -269,9 +250,7 @@ func initDataNodeApply(d lxd.InstanceServer, config 
initDataNode) (func(), error
                        }
 
                        // Setup reverter
-                       reverts = append(reverts, func() {
-                               d.UpdateProfile(currentProfile.Name, 
currentProfile.Writable(), "")
-                       })
+                       revert.Add(func() { 
d.UpdateProfile(currentProfile.Name, currentProfile.Writable(), "") })
 
                        // Prepare the update
                        newProfile := api.ProfilePut{}
@@ -319,7 +298,7 @@ func initDataNodeApply(d lxd.InstanceServer, config 
initDataNode) (func(), error
                        if !shared.StringInSlice(profile.Name, profileNames) {
                                err := createProfile(profile)
                                if err != nil {
-                                       return revert, err
+                                       return err
                                }
 
                                continue
@@ -328,12 +307,13 @@ func initDataNodeApply(d lxd.InstanceServer, config 
initDataNode) (func(), error
                        // Existing profile
                        err := updateProfile(profile)
                        if err != nil {
-                               return revert, err
+                               return err
                        }
                }
        }
 
-       return nil, nil
+       revert.Success()
+       return nil
 }
 
 // Helper to initialize LXD clustering.

From 6127ed7e543590e5ffaa00d431010c7a49851c28 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 26 Aug 2020 11:40:19 +0100
Subject: [PATCH 03/15] lxd/cluster/connect: Adds UserAgentNotifier constant

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/cluster/connect.go | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/lxd/cluster/connect.go b/lxd/cluster/connect.go
index 4773d7e354..bce7206595 100644
--- a/lxd/cluster/connect.go
+++ b/lxd/cluster/connect.go
@@ -18,11 +18,15 @@ import (
        "github.com/lxc/lxd/shared/version"
 )
 
+// UserAgentNotifier used to distinguish between a regular client request and 
an internal cluster request when
+// notifying other nodes of a cluster change.
+const UserAgentNotifier = "lxd-cluster-notifier"
+
 // Connect is a convenience around lxd.ConnectLXD that configures the client
 // with the correct parameters for node-to-node communication.
 //
 // If 'notify' switch is true, then the user agent will be set to the special
-// value 'lxd-cluster-notifier', which can be used in some cases to distinguish
+// to the UserAgentNotifier value, which can be used in some cases to 
distinguish
 // between a regular client request and an internal cluster request.
 func Connect(address string, cert *shared.CertInfo, notify bool) 
(lxd.InstanceServer, error) {
        // Wait for a connection to the events API first for non-notify 
connections.
@@ -54,7 +58,7 @@ func Connect(address string, cert *shared.CertInfo, notify 
bool) (lxd.InstanceSe
                UserAgent:     version.UserAgent,
        }
        if notify {
-               args.UserAgent = "lxd-cluster-notifier"
+               args.UserAgent = UserAgentNotifier
        }
 
        url := fmt.Sprintf("https://%s";, address)

From 6f2c50c072fadea92baf8be2cb72f2f95bf645cb Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 26 Aug 2020 11:44:18 +0100
Subject: [PATCH 04/15] lxd/cluster/connect: Adds UserAgentJoiner constant

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

diff --git a/lxd/cluster/connect.go b/lxd/cluster/connect.go
index bce7206595..523264bb6b 100644
--- a/lxd/cluster/connect.go
+++ b/lxd/cluster/connect.go
@@ -22,6 +22,10 @@ import (
 // notifying other nodes of a cluster change.
 const UserAgentNotifier = "lxd-cluster-notifier"
 
+// UserAgentJoiner used to distinguish between a regular client request and an 
internal cluster request when
+// joining a node to a cluster.
+const UserAgentJoiner = "lxd-cluster-joiner"
+
 // Connect is a convenience around lxd.ConnectLXD that configures the client
 // with the correct parameters for node-to-node communication.
 //

From ac1dffda59deba257c4ca6daee47314cca1d27d9 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 26 Aug 2020 14:25:53 +0100
Subject: [PATCH 05/15] lxd/cluster/connect: Adds ClientType type and
 UserAgentClientType function

 - Defines client type constants for normal, notifier and joiner types.
 - Adds a function UserAgentClientType for converting client user agent to 
client type.

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

diff --git a/lxd/cluster/connect.go b/lxd/cluster/connect.go
index 523264bb6b..7ed1dd0773 100644
--- a/lxd/cluster/connect.go
+++ b/lxd/cluster/connect.go
@@ -26,6 +26,30 @@ const UserAgentNotifier = "lxd-cluster-notifier"
 // joining a node to a cluster.
 const UserAgentJoiner = "lxd-cluster-joiner"
 
+// ClientType indicates which sort of client type is being used.
+type ClientType string
+
+// ClientTypeNotifier cluster notification client.
+const ClientTypeNotifier ClientType = "notifier"
+
+// ClientTypeJoiner cluster joiner client.
+const ClientTypeJoiner ClientType = "joiner"
+
+// ClientTypeNormal normal client.
+const ClientTypeNormal ClientType = "normal"
+
+// UserAgentClientType converts user agent to client type.
+func UserAgentClientType(userAgent string) ClientType {
+       switch userAgent {
+       case UserAgentNotifier:
+               return ClientTypeNotifier
+       case UserAgentJoiner:
+               return ClientTypeJoiner
+       }
+
+       return ClientTypeNormal
+}
+
 // Connect is a convenience around lxd.ConnectLXD that configures the client
 // with the correct parameters for node-to-node communication.
 //

From 5c29460f2af4edf8f91c3a4ece1c7bafe04addb5 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 26 Aug 2020 11:40:47 +0100
Subject: [PATCH 06/15] lxd/api: Updates isClusterNotification to use
 cluster.UserAgentNotifier

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

diff --git a/lxd/api.go b/lxd/api.go
index faa0e4ab58..2bdcba49d2 100644
--- a/lxd/api.go
+++ b/lxd/api.go
@@ -118,7 +118,7 @@ func setCORSHeaders(rw http.ResponseWriter, req 
*http.Request, config *cluster.C
 // notifying us of some user-initiated API request that needs some action to be
 // taken on this node as well.
 func isClusterNotification(r *http.Request) bool {
-       return r.Header.Get("User-Agent") == "lxd-cluster-notifier"
+       return r.Header.Get("User-Agent") == cluster.UserAgentNotifier
 }
 
 // projectParam returns the project query parameter from the given request or 
"default" if parameter is not set.

From 8292a901f97e5807cfefe86bf1b0e8b794b19597 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 26 Aug 2020 11:42:20 +0100
Subject: [PATCH 07/15] lxd/api/cluster: clusterInitMember comments

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

diff --git a/lxd/api_cluster.go b/lxd/api_cluster.go
index 32d3e5ac1a..471aaa04e0 100644
--- a/lxd/api_cluster.go
+++ b/lxd/api_cluster.go
@@ -686,11 +686,9 @@ func clusterPutDisable(d *Daemon) response.Response {
        return response.EmptySyncResponse
 }
 
-// Initialize storage pools and networks on this node.
-//
-// We pass to LXD client instances, one connected to ourselves (the joining
-// node) and one connected to the target cluster node to join.
-func clusterInitMember(d, client lxd.InstanceServer, memberConfig 
[]api.ClusterMemberConfigKey) error {
+// clusterInitMember initialises storage pools and networks on this node. We 
pass two LXD client instances, one
+// connected to ourselves (the joining node) and one connected to the target 
cluster node to join.
+func clusterInitMember(d lxd.InstanceServer, client lxd.InstanceServer, 
memberConfig []api.ClusterMemberConfigKey) error {
        data := initDataNode{}
 
        // Fetch all pools currently defined in the cluster.

From cffeed2766216e11a0764a354a3e588fd3fe6052 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 26 Aug 2020 11:42:32 +0100
Subject: [PATCH 08/15] lxd/api/cluster: initDataNodeApply usage

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

diff --git a/lxd/api_cluster.go b/lxd/api_cluster.go
index 471aaa04e0..55a70d059c 100644
--- a/lxd/api_cluster.go
+++ b/lxd/api_cluster.go
@@ -785,9 +785,8 @@ func clusterInitMember(d lxd.InstanceServer, client 
lxd.InstanceServer, memberCo
                data.Networks = append(data.Networks, post)
        }
 
-       revert, err := initDataNodeApply(d, data)
+       err = initDataNodeApply(d, data)
        if err != nil {
-               revert()
                return errors.Wrap(err, "Failed to initialize storage pools and 
networks")
        }
 

From 913140ecdbc5de420e841fe19b8e20459bbba2ec Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 26 Aug 2020 11:42:47 +0100
Subject: [PATCH 09/15] lxd/main/init: initDataNodeApply usage

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

diff --git a/lxd/main_init.go b/lxd/main_init.go
index 62047c8f91..eef6d370bc 100644
--- a/lxd/main_init.go
+++ b/lxd/main_init.go
@@ -149,9 +149,8 @@ func (c *cmdInit) Run(cmd *cobra.Command, args []string) 
error {
                return nil
        }
 
-       revert, err := initDataNodeApply(d, config.Node)
+       err = initDataNodeApply(d, config.Node)
        if err != nil {
-               revert()
                return err
        }
 

From df86ab953d8e03735ccc40607d0131d6224166ed Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 26 Aug 2020 11:44:40 +0100
Subject: [PATCH 10/15] lxd/api/cluster: Updates clusterPutJoin to use
 cluster.UserAgentJoiner when sending requests to local node

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

diff --git a/lxd/api_cluster.go b/lxd/api_cluster.go
index 55a70d059c..a9345985c4 100644
--- a/lxd/api_cluster.go
+++ b/lxd/api_cluster.go
@@ -386,7 +386,7 @@ func clusterPutJoin(d *Daemon, req api.ClusterPut) 
response.Response {
                // As ServerAddress field is required to be set it means that 
we're using the new join API
                // introduced with the 'clustering_join' extension.
                // Connect to ourselves to initialize storage pools and 
networks using the API.
-               localClient, err := lxd.ConnectLXDUnix(d.UnixSocket(), nil)
+               localClient, err := lxd.ConnectLXDUnix(d.UnixSocket(), 
&lxd.ConnectionArgs{UserAgent: cluster.UserAgentJoiner})
                if err != nil {
                        return errors.Wrap(err, "Failed to connect to local 
LXD")
                }

From 585095fb70407f3dff2f1ffacd8d89dfdd025ff4 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 26 Aug 2020 14:27:18 +0100
Subject: [PATCH 11/15] lxd/network/network/interfaces: Replaces
 clusterNotification bool with cluster.ClientType

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

diff --git a/lxd/network/network_interface.go b/lxd/network/network_interface.go
index dfb9e3b470..69e787a14a 100644
--- a/lxd/network/network_interface.go
+++ b/lxd/network/network_interface.go
@@ -30,11 +30,11 @@ type Network interface {
        DHCPv6Ranges() []shared.IPRange
 
        // Actions.
-       Create(clusterNotification bool) error
+       Create(clientType cluster.ClientType) error
        Start() error
        Stop() error
        Rename(name string) error
-       Update(newNetwork api.NetworkPut, targetNode string, 
clusterNotification bool) error
+       Update(newNetwork api.NetworkPut, targetNode string, clientType 
cluster.ClientType) error
        HandleHeartbeat(heartbeatData *cluster.APIHeartbeat) error
-       Delete(clusterNotification bool) error
+       Delete(clientType cluster.ClientType) error
 }

From be3d1bb5426bf73a6bf7dfbf4fec105e39e518ed Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 26 Aug 2020 14:27:53 +0100
Subject: [PATCH 12/15] lxd/network/driver/common: cluster.ClientType usage

And improved logging.

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

diff --git a/lxd/network/driver_common.go b/lxd/network/driver_common.go
index 296106b82d..41197225f6 100644
--- a/lxd/network/driver_common.go
+++ b/lxd/network/driver_common.go
@@ -222,14 +222,14 @@ func (n *common) DHCPv6Ranges() []shared.IPRange {
 }
 
 // update the internal config variables, and if not cluster notification, 
notifies all nodes and updates database.
-func (n *common) update(applyNetwork api.NetworkPut, targetNode string, 
clusterNotification bool) error {
+func (n *common) update(applyNetwork api.NetworkPut, targetNode string, 
clientType cluster.ClientType) error {
        // Update internal config before database has been updated (so that if 
update is a notification we apply
        // the config being supplied and not that in the database).
        n.init(n.state, n.id, n.name, n.netType, applyNetwork.Description, 
applyNetwork.Config, n.status)
 
        // If this update isn't coming via a cluster notification itself, then 
notify all nodes of change and then
        // update the database.
-       if !clusterNotification {
+       if clientType != cluster.ClientTypeNotifier {
                if targetNode == "" {
                        // Notify all other nodes to update the network if no 
target specified.
                        notifier, err := cluster.NewNotifier(n.state, 
n.state.Endpoints.NetworkCert(), cluster.NotifyAll)
@@ -342,9 +342,9 @@ func (n *common) rename(newName string) error {
 }
 
 // delete the network from the database if clusterNotification is false.
-func (n *common) delete(clusterNotification bool) error {
+func (n *common) delete(clientType cluster.ClientType) error {
        // Only delete database record if not cluster notification.
-       if !clusterNotification {
+       if clientType != cluster.ClientTypeNotifier {
                // Notify all other nodes. If any node is down, an error will 
be returned.
                notifier, err := cluster.NewNotifier(n.state, 
n.state.Endpoints.NetworkCert(), cluster.NotifyAll)
                if err != nil {
@@ -373,7 +373,9 @@ func (n *common) delete(clusterNotification bool) error {
 }
 
 // Create is a no-op.
-func (n *common) Create(clusterNotification bool) error {
+func (n *common) Create(clientType cluster.ClientType) error {
+       n.logger.Debug("Create", log.Ctx{"clientType": clientType, "config": 
n.config})
+
        return nil
 }
 

From 03cd073e7d48cbd30445bb1ab1c6fd022074abdb Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 26 Aug 2020 14:28:38 +0100
Subject: [PATCH 13/15] lxd/network/driver: cluster.ClientType usage

And improved logging.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/network/driver_bridge.go  | 18 +++++++++++-------
 lxd/network/driver_macvlan.go | 19 ++++++++++++-------
 lxd/network/driver_sriov.go   | 19 ++++++++++++-------
 3 files changed, 35 insertions(+), 21 deletions(-)

diff --git a/lxd/network/driver_bridge.go b/lxd/network/driver_bridge.go
index 562fe48280..8ef1e612e4 100644
--- a/lxd/network/driver_bridge.go
+++ b/lxd/network/driver_bridge.go
@@ -384,8 +384,8 @@ func (n *bridge) isRunning() bool {
 }
 
 // Delete deletes a network.
-func (n *bridge) Delete(clusterNotification bool) error {
-       n.logger.Debug("Delete", log.Ctx{"clusterNotification": 
clusterNotification})
+func (n *bridge) Delete(clientType cluster.ClientType) error {
+       n.logger.Debug("Delete", log.Ctx{"clientType": clientType})
 
        // Bring the network down.
        if n.isRunning() {
@@ -401,7 +401,7 @@ func (n *bridge) Delete(clusterNotification bool) error {
                return err
        }
 
-       return n.common.delete(clusterNotification)
+       return n.common.delete(clientType)
 }
 
 // Rename renames a network.
@@ -452,6 +452,8 @@ func (n *bridge) Rename(newName string) error {
 
 // Start starts the network.
 func (n *bridge) Start() error {
+       n.logger.Debug("Start")
+
        return n.setup(nil)
 }
 
@@ -1420,6 +1422,8 @@ func (n *bridge) setup(oldConfig map[string]string) error 
{
 
 // Stop stops the network.
 func (n *bridge) Stop() error {
+       n.logger.Debug("Stop")
+
        if !n.isRunning() {
                return nil
        }
@@ -1491,8 +1495,8 @@ func (n *bridge) Stop() error {
 
 // Update updates the network. Accepts notification boolean indicating if this 
update request is coming from a
 // cluster notification, in which case do not update the database, just apply 
local changes needed.
-func (n *bridge) Update(newNetwork api.NetworkPut, targetNode string, 
clusterNotification bool) error {
-       n.logger.Debug("Update", log.Ctx{"clusterNotification": 
clusterNotification, "newNetwork": newNetwork})
+func (n *bridge) Update(newNetwork api.NetworkPut, targetNode string, 
clientType cluster.ClientType) error {
+       n.logger.Debug("Update", log.Ctx{"clientType": clientType, 
"newNetwork": newNetwork})
 
        // Populate default values if they are missing.
        err := n.fillConfig(newNetwork.Config)
@@ -1515,7 +1519,7 @@ func (n *bridge) Update(newNetwork api.NetworkPut, 
targetNode string, clusterNot
        // Define a function which reverts everything.
        revert.Add(func() {
                // Reset changes to all nodes and database.
-               n.common.update(oldNetwork, targetNode, clusterNotification)
+               n.common.update(oldNetwork, targetNode, clientType)
 
                // Reset any change that was made to local bridge.
                n.setup(newNetwork.Config)
@@ -1553,7 +1557,7 @@ func (n *bridge) Update(newNetwork api.NetworkPut, 
targetNode string, clusterNot
        }
 
        // Apply changes to database.
-       err = n.common.update(newNetwork, targetNode, clusterNotification)
+       err = n.common.update(newNetwork, targetNode, clientType)
        if err != nil {
                return err
        }
diff --git a/lxd/network/driver_macvlan.go b/lxd/network/driver_macvlan.go
index 6195c6c9f0..5798f06c65 100644
--- a/lxd/network/driver_macvlan.go
+++ b/lxd/network/driver_macvlan.go
@@ -3,6 +3,7 @@ package network
 import (
        "fmt"
 
+       "github.com/lxc/lxd/lxd/cluster"
        "github.com/lxc/lxd/lxd/revert"
        "github.com/lxc/lxd/shared/api"
        log "github.com/lxc/lxd/shared/log15"
@@ -33,9 +34,9 @@ func (n *macvlan) Validate(config map[string]string) error {
 }
 
 // Delete deletes a network.
-func (n *macvlan) Delete(clusterNotification bool) error {
-       n.logger.Debug("Delete", log.Ctx{"clusterNotification": 
clusterNotification})
-       return n.common.delete(clusterNotification)
+func (n *macvlan) Delete(clientType cluster.ClientType) error {
+       n.logger.Debug("Delete", log.Ctx{"clientType": clientType})
+       return n.common.delete(clientType)
 }
 
 // Rename renames a network.
@@ -63,6 +64,8 @@ func (n *macvlan) Rename(newName string) error {
 
 // Start starts is a no-op.
 func (n *macvlan) Start() error {
+       n.logger.Debug("Start")
+
        if n.status == api.NetworkStatusPending {
                return fmt.Errorf("Cannot start pending network")
        }
@@ -72,13 +75,15 @@ func (n *macvlan) Start() error {
 
 // Stop stops is a no-op.
 func (n *macvlan) Stop() error {
+       n.logger.Debug("Stop")
+
        return nil
 }
 
 // Update updates the network. Accepts notification boolean indicating if this 
update request is coming from a
 // cluster notification, in which case do not update the database, just apply 
local changes needed.
-func (n *macvlan) Update(newNetwork api.NetworkPut, targetNode string, 
clusterNotification bool) error {
-       n.logger.Debug("Update", log.Ctx{"clusterNotification": 
clusterNotification, "newNetwork": newNetwork})
+func (n *macvlan) Update(newNetwork api.NetworkPut, targetNode string, 
clientType cluster.ClientType) error {
+       n.logger.Debug("Update", log.Ctx{"clientType": clientType, 
"newNetwork": newNetwork})
 
        dbUpdateNeeeded, _, oldNetwork, err := 
n.common.configChanged(newNetwork)
        if err != nil {
@@ -95,11 +100,11 @@ func (n *macvlan) Update(newNetwork api.NetworkPut, 
targetNode string, clusterNo
        // Define a function which reverts everything.
        revert.Add(func() {
                // Reset changes to all nodes and database.
-               n.common.update(oldNetwork, targetNode, clusterNotification)
+               n.common.update(oldNetwork, targetNode, clientType)
        })
 
        // Apply changes to database.
-       err = n.common.update(newNetwork, targetNode, clusterNotification)
+       err = n.common.update(newNetwork, targetNode, clientType)
        if err != nil {
                return err
        }
diff --git a/lxd/network/driver_sriov.go b/lxd/network/driver_sriov.go
index 2f3483ed75..034ede797a 100644
--- a/lxd/network/driver_sriov.go
+++ b/lxd/network/driver_sriov.go
@@ -3,6 +3,7 @@ package network
 import (
        "fmt"
 
+       "github.com/lxc/lxd/lxd/cluster"
        "github.com/lxc/lxd/lxd/revert"
        "github.com/lxc/lxd/shared/api"
        log "github.com/lxc/lxd/shared/log15"
@@ -33,9 +34,9 @@ func (n *sriov) Validate(config map[string]string) error {
 }
 
 // Delete deletes a network.
-func (n *sriov) Delete(clusterNotification bool) error {
-       n.logger.Debug("Delete", log.Ctx{"clusterNotification": 
clusterNotification})
-       return n.common.delete(clusterNotification)
+func (n *sriov) Delete(clientType cluster.ClientType) error {
+       n.logger.Debug("Delete", log.Ctx{"clientType": clientType})
+       return n.common.delete(clientType)
 }
 
 // Rename renames a network.
@@ -63,6 +64,8 @@ func (n *sriov) Rename(newName string) error {
 
 // Start starts is a no-op.
 func (n *sriov) Start() error {
+       n.logger.Debug("Start")
+
        if n.status == api.NetworkStatusPending {
                return fmt.Errorf("Cannot start pending network")
        }
@@ -72,13 +75,15 @@ func (n *sriov) Start() error {
 
 // Stop stops is a no-op.
 func (n *sriov) Stop() error {
+       n.logger.Debug("Stop")
+
        return nil
 }
 
 // Update updates the network. Accepts notification boolean indicating if this 
update request is coming from a
 // cluster notification, in which case do not update the database, just apply 
local changes needed.
-func (n *sriov) Update(newNetwork api.NetworkPut, targetNode string, 
clusterNotification bool) error {
-       n.logger.Debug("Update", log.Ctx{"clusterNotification": 
clusterNotification, "newNetwork": newNetwork})
+func (n *sriov) Update(newNetwork api.NetworkPut, targetNode string, 
clientType cluster.ClientType) error {
+       n.logger.Debug("Update", log.Ctx{"clientType": clientType, 
"newNetwork": newNetwork})
 
        dbUpdateNeeeded, _, oldNetwork, err := 
n.common.configChanged(newNetwork)
        if err != nil {
@@ -95,11 +100,11 @@ func (n *sriov) Update(newNetwork api.NetworkPut, 
targetNode string, clusterNoti
        // Define a function which reverts everything.
        revert.Add(func() {
                // Reset changes to all nodes and database.
-               n.common.update(oldNetwork, targetNode, clusterNotification)
+               n.common.update(oldNetwork, targetNode, clientType)
        })
 
        // Apply changes to database.
-       err = n.common.update(newNetwork, targetNode, clusterNotification)
+       err = n.common.update(newNetwork, targetNode, clientType)
        if err != nil {
                return err
        }

From 0d245ffd733f9ff10adc97d843398dff13a1a7b9 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 26 Aug 2020 14:30:42 +0100
Subject: [PATCH 14/15] lxd/network/driver/ovn: cluster.ClientType usage

 - Differentiates between normal and cluster joiner client type requests and 
only sets up logical network on normal requests.
 - And improved logging.

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

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index 29174efff7..4066ee366c 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -728,11 +728,11 @@ func (n *ovn) fillConfig(config map[string]string) error {
 }
 
 // Create sets up network in OVN Northbound database.
-func (n *ovn) Create(clusterNotification bool) error {
-       n.logger.Debug("Create", log.Ctx{"clusterNotification": 
clusterNotification, "config": n.config})
+func (n *ovn) Create(clientType cluster.ClientType) error {
+       n.logger.Debug("Create", log.Ctx{"clientType": clientType, "config": 
n.config})
 
        // We only need to setup the OVN Northbound database once, not on every 
clustered node.
-       if !clusterNotification {
+       if clientType == cluster.ClientTypeNormal {
                err := n.setup(false)
                if err != nil {
                        return err
@@ -1054,10 +1054,10 @@ func (n *ovn) setup(update bool) error {
 }
 
 // Delete deletes a network.
-func (n *ovn) Delete(clusterNotification bool) error {
-       n.logger.Debug("Delete", log.Ctx{"clusterNotification": 
clusterNotification})
+func (n *ovn) Delete(clientType cluster.ClientType) error {
+       n.logger.Debug("Delete", log.Ctx{"clientType": clientType})
 
-       if !clusterNotification {
+       if clientType == cluster.ClientTypeNormal {
                client, err := n.getClient()
                if err != nil {
                        return err
@@ -1116,7 +1116,7 @@ func (n *ovn) Delete(clusterNotification bool) error {
                return err
        }
 
-       return n.common.delete(clusterNotification)
+       return n.common.delete(clientType)
 }
 
 // Rename renames a network.
@@ -1144,6 +1144,8 @@ func (n *ovn) Rename(newName string) error {
 
 // Start starts configures the local OVS parent uplink port.
 func (n *ovn) Start() error {
+       n.logger.Debug("Start")
+
        if n.status == api.NetworkStatusPending {
                return fmt.Errorf("Cannot start pending network")
        }
@@ -1158,13 +1160,15 @@ func (n *ovn) Start() error {
 
 // Stop stops is a no-op.
 func (n *ovn) Stop() error {
+       n.logger.Debug("Stop")
+
        return nil
 }
 
 // Update updates the network. Accepts notification boolean indicating if this 
update request is coming from a
 // cluster notification, in which case do not update the database, just apply 
local changes needed.
-func (n *ovn) Update(newNetwork api.NetworkPut, targetNode string, 
clusterNotification bool) error {
-       n.logger.Debug("Update", log.Ctx{"clusterNotification": 
clusterNotification, "newNetwork": newNetwork})
+func (n *ovn) Update(newNetwork api.NetworkPut, targetNode string, clientType 
cluster.ClientType) error {
+       n.logger.Debug("Update", log.Ctx{"clientType": clientType, 
"newNetwork": newNetwork})
 
        // Populate default values if they are missing.
        err := n.fillConfig(newNetwork.Config)
@@ -1187,22 +1191,22 @@ func (n *ovn) Update(newNetwork api.NetworkPut, 
targetNode string, clusterNotifi
        // Define a function which reverts everything.
        revert.Add(func() {
                // Reset changes to all nodes and database.
-               n.common.update(oldNetwork, targetNode, clusterNotification)
+               n.common.update(oldNetwork, targetNode, clientType)
 
                // Reset any change that was made to logical network.
-               if !clusterNotification {
+               if clientType == cluster.ClientTypeNormal {
                        n.setup(true)
                }
        })
 
        // Apply changes to database.
-       err = n.common.update(newNetwork, targetNode, clusterNotification)
+       err = n.common.update(newNetwork, targetNode, clientType)
        if err != nil {
                return err
        }
 
-       // Restart the logical network if needed.
-       if len(changedKeys) > 0 && !clusterNotification {
+       // Re-setup the logical network if needed.
+       if len(changedKeys) > 0 && clientType == cluster.ClientTypeNormal {
                err = n.setup(true)
                if err != nil {
                        return err

From 1a2375d6e54d2dfb4fed0ac32fc718e57107621c Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 26 Aug 2020 14:32:54 +0100
Subject: [PATCH 15/15] lxd/networks: cluster.ClientType usage

Replaces cluster notification concept.

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

diff --git a/lxd/networks.go b/lxd/networks.go
index 60bd4a8642..df4f027f33 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -142,10 +142,12 @@ func networksPost(d *Daemon, r *http.Request) 
response.Response {
        url := fmt.Sprintf("/%s/networks/%s", version.APIVersion, req.Name)
        resp := response.SyncResponseLocation(true, nil, url)
 
+       clientType := cluster.UserAgentClientType(r.Header.Get("User-Agent"))
+
        if isClusterNotification(r) {
                // This is an internal request which triggers the actual 
creation of the network across all nodes
                // after they have been previously defined.
-               err = doNetworksCreate(d, req, true)
+               err = doNetworksCreate(d, req, clientType)
                if err != nil {
                        return response.SmartError(err)
                }
@@ -181,7 +183,7 @@ func networksPost(d *Daemon, r *http.Request) 
response.Response {
        }
 
        if count > 1 {
-               err = networksPostCluster(d, req)
+               err = networksPostCluster(d, req, clientType)
                if err != nil {
                        return response.SmartError(err)
                }
@@ -217,8 +219,7 @@ func networksPost(d *Daemon, r *http.Request) 
response.Response {
                d.cluster.DeleteNetwork(req.Name)
        })
 
-       // Create network and pass false to clusterNotification so the database 
record is removed on error.
-       err = doNetworksCreate(d, req, false)
+       err = doNetworksCreate(d, req, clientType)
        if err != nil {
                return response.SmartError(err)
        }
@@ -227,7 +228,7 @@ func networksPost(d *Daemon, r *http.Request) 
response.Response {
        return resp
 }
 
-func networksPostCluster(d *Daemon, req api.NetworksPost) error {
+func networksPostCluster(d *Daemon, req api.NetworksPost, clientType 
cluster.ClientType) error {
        // Check that no node-specific config key has been defined.
        for key := range req.Config {
                if shared.StringInSlice(key, db.NodeSpecificNetworkConfig) {
@@ -316,7 +317,7 @@ func networksPostCluster(d *Daemon, req api.NetworksPost) 
error {
                return err
        }
 
-       err = doNetworksCreate(d, nodeReq, false)
+       err = doNetworksCreate(d, nodeReq, clientType)
        if err != nil {
                return err
        }
@@ -344,7 +345,7 @@ func networksPostCluster(d *Daemon, req api.NetworksPost) 
error {
 
 // Create the network on the system. The clusterNotification flag is used to 
indicate whether creation request
 // is coming from a cluster notification (and if so we should not delete the 
database record on error).
-func doNetworksCreate(d *Daemon, req api.NetworksPost, clusterNotification 
bool) error {
+func doNetworksCreate(d *Daemon, req api.NetworksPost, clientType 
cluster.ClientType) error {
        // Start the network.
        n, err := network.LoadByName(d.State(), req.Name)
        if err != nil {
@@ -358,14 +359,14 @@ func doNetworksCreate(d *Daemon, req api.NetworksPost, 
clusterNotification bool)
        }
 
        // Run initial creation setup for the network driver.
-       err = n.Create(clusterNotification)
+       err = n.Create(clientType)
        if err != nil {
                return err
        }
 
        err = n.Start()
        if err != nil {
-               n.Delete(clusterNotification)
+               n.Delete(clientType)
                return err
        }
 
@@ -535,6 +536,8 @@ func networkDelete(d *Daemon, r *http.Request) 
response.Response {
                return response.NotFound(err)
        }
 
+       clientType := cluster.UserAgentClientType(r.Header.Get("User-Agent"))
+
        clusterNotification := isClusterNotification(r)
        if !clusterNotification {
                // Sanity checks.
@@ -549,7 +552,7 @@ func networkDelete(d *Daemon, r *http.Request) 
response.Response {
        }
 
        // Delete the network.
-       err = n.Delete(clusterNotification)
+       err = n.Delete(clientType)
        if err != nil {
                return response.SmartError(err)
        }
@@ -681,7 +684,9 @@ func networkPut(d *Daemon, r *http.Request) 
response.Response {
                }
        }
 
-       return doNetworkUpdate(d, name, req, targetNode, 
isClusterNotification(r), r.Method, clustered)
+       clientType := cluster.UserAgentClientType(r.Header.Get("User-Agent"))
+
+       return doNetworkUpdate(d, name, req, targetNode, clientType, r.Method, 
clustered)
 }
 
 func networkPatch(d *Daemon, r *http.Request) response.Response {
@@ -690,7 +695,7 @@ func networkPatch(d *Daemon, r *http.Request) 
response.Response {
 
 // doNetworkUpdate loads the current local network config, merges with the 
requested network config, validates
 // and applies the changes. Will also notify other cluster nodes of non-node 
specific config if needed.
-func doNetworkUpdate(d *Daemon, name string, req api.NetworkPut, targetNode 
string, clusterNotification bool, httpMethod string, clustered bool) 
response.Response {
+func doNetworkUpdate(d *Daemon, name string, req api.NetworkPut, targetNode 
string, clientType cluster.ClientType, httpMethod string, clustered bool) 
response.Response {
        // Load the local node-specific network.
        n, err := network.LoadByName(d.State(), name)
        if err != nil {
@@ -730,7 +735,7 @@ func doNetworkUpdate(d *Daemon, name string, req 
api.NetworkPut, targetNode stri
        }
 
        // Apply the new configuration (will also notify other cluster nodes if 
needed).
-       err = n.Update(req, targetNode, clusterNotification)
+       err = n.Update(req, targetNode, clientType)
        if err != nil {
                return response.SmartError(err)
        }
_______________________________________________
lxc-devel mailing list
lxc-devel@lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to