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