The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/8244
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 doing a `lxc network create` for subsequent attempts after failed initial attempt, avoid creating duplicate global config by ignoring global config supplied on subsequent attempts.
From 689ca1fd3da951dcc790ed8d7fdcfd5b3c3fb3f1 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Fri, 11 Dec 2020 17:19:49 +0000 Subject: [PATCH 1/7] lxd/db/networks: Adds duplicate key detection to getNetworkConfig Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/db/networks.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lxd/db/networks.go b/lxd/db/networks.go index dece3638a6..e6f75119ca 100644 --- a/lxd/db/networks.go +++ b/lxd/db/networks.go @@ -670,6 +670,11 @@ func (c *Cluster) getNetworkConfig(id int64) (map[string]string, error) { key = r[0].(string) value = r[1].(string) + _, found := config[key] + if found { + return nil, fmt.Errorf("Duplicate config row found for key %q for network ID %d", key, id) + } + config[key] = value } From fec13a6a73b02ea609cdca005ece4538ae758615 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Fri, 11 Dec 2020 17:20:04 +0000 Subject: [PATCH 2/7] lxd/network/driver/ovn: Reject instance port start if cannot find DHCP options Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/network/driver_ovn.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index 4d7a6a3851..6cd162accb 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -2151,6 +2151,10 @@ func (n *ovn) InstanceDevicePortAdd(instanceUUID string, instanceName string, de if err != nil { return "", err } + + if dhcpV4ID == "" { + return "", fmt.Errorf("Could not find DHCPv4 options for instance port") + } } if dhcpv6Subnet != nil { @@ -2159,6 +2163,10 @@ func (n *ovn) InstanceDevicePortAdd(instanceUUID string, instanceName string, de return "", err } + if dhcpV4ID == "" { + return "", fmt.Errorf("Could not find DHCPv6 options for instance port") + } + // If port isn't going to have fully dynamic IPs allocated by OVN, and instead only static IPv4 // addresses have been added, then add an EUI64 static IPv6 address so that the switch port has an // IPv6 address that will be used to generate a DNS record. This works around a limitation in OVN From c3c36e878ad6b02078b8dace1892527bec553e73 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Fri, 11 Dec 2020 17:20:33 +0000 Subject: [PATCH 3/7] lxd/networks: doNetworksCreate usage Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/networks.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lxd/networks.go b/lxd/networks.go index daf238facf..cf28c7d846 100644 --- a/lxd/networks.go +++ b/lxd/networks.go @@ -198,9 +198,14 @@ func networksPost(d *Daemon, r *http.Request) response.Response { clientType := request.UserAgentClientType(r.Header.Get("User-Agent")) if isClusterNotification(r) { + n, err := network.LoadByName(d.State(), projectName, req.Name) + if err != nil { + return response.SmartError(err) + } + // 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, projectName, req, clientType) + err = doNetworksCreate(d, n, clientType) if err != nil { return response.SmartError(err) } From 319053c314e310ef561c5de381d4ed5e118b0e86 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Fri, 11 Dec 2020 17:20:52 +0000 Subject: [PATCH 4/7] lxd/networks: When auto creating pending nodes, don't pass global config into DB function in networksPost We don't want to store global config yet and this can cause duplicates. Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/networks.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lxd/networks.go b/lxd/networks.go index cf28c7d846..7701b03d6c 100644 --- a/lxd/networks.go +++ b/lxd/networks.go @@ -256,7 +256,8 @@ func networksPost(d *Daemon, r *http.Request) response.Response { } for _, node := range nodes { - err = tx.CreatePendingNetwork(node.Name, projectName, req.Name, netType.DBType(), req.Config) + // Don't pass in any node-specific config. + err = tx.CreatePendingNetwork(node.Name, projectName, req.Name, netType.DBType(), nil) if err != nil && errors.Cause(err) != db.ErrAlreadyDefined { return errors.Wrapf(err, "Failed creating pending network for node %q", node.Name) } From 5450215ae9344dcb5495420bdb402f1d82b00ee0 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Fri, 11 Dec 2020 17:21:35 +0000 Subject: [PATCH 5/7] lxd/networks: doNetworksCreate usage Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/networks.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lxd/networks.go b/lxd/networks.go index 7701b03d6c..3f334e5a8d 100644 --- a/lxd/networks.go +++ b/lxd/networks.go @@ -304,7 +304,12 @@ func networksPost(d *Daemon, r *http.Request) response.Response { } revert.Add(func() { d.cluster.DeleteNetwork(projectName, req.Name) }) - err = doNetworksCreate(d, projectName, req, clientType) + n, err := network.LoadByName(d.State(), projectName, req.Name) + if err != nil { + return response.SmartError(err) + } + + err = doNetworksCreate(d, n, clientType) if err != nil { return response.SmartError(err) } From bda2c2d9ffc3e2e68779e624df8e20184e6bc4b7 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Fri, 11 Dec 2020 17:22:12 +0000 Subject: [PATCH 6/7] lxd/networks: Updates networksPostCluster to detect existing global config and skip create if needed Allows network re-creation attempts after failed creation without causing duplicate global config. Also re-works notifications so that stored global and node config is used, rather than what was sent in the subsequent request (will be the same on first time). Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/networks.go | 57 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/lxd/networks.go b/lxd/networks.go index 3f334e5a8d..fef87b60e8 100644 --- a/lxd/networks.go +++ b/lxd/networks.go @@ -339,14 +339,8 @@ func networksPostCluster(d *Daemon, projectName string, req api.NetworksPost, cl return fmt.Errorf("Requested network type %q doesn't match type in existing database record %q", req.Type, netInfo.Type) } - // Add default values. - err = netType.FillConfig(req.Config) - if err != nil { - return err - } - // Check that the network is properly defined, get the node-specific configs and merge with global config. - var configs map[string]map[string]string + var nodeConfigs map[string]map[string]string var nodeName string var networkID int64 err = d.cluster.Transaction(func(tx *db.ClusterTx) error { @@ -357,7 +351,7 @@ func networksPostCluster(d *Daemon, projectName string, req api.NetworksPost, cl } // Fetch the node-specific configs. - configs, err = tx.NetworkNodeConfigs(networkID) + nodeConfigs, err = tx.NetworkNodeConfigs(networkID) if err != nil { return err } @@ -368,6 +362,20 @@ func networksPostCluster(d *Daemon, projectName string, req api.NetworksPost, cl return err } + // Check if any global config exists already, if so we should not create global config again. + for key := range netInfo.Config { + if !shared.StringInSlice(key, db.NodeSpecificNetworkConfig) { + logger.Warn("Skipping global network create as global config already exists", log.Ctx{"project": projectName, "network": req.Name}) + return nil + } + } + + // Add default values if we are inserting global config for first time. + err = netType.FillConfig(req.Config) + if err != nil { + return err + } + // Insert the global config keys. return tx.CreateNetworkConfig(networkID, 0, req.Config) }) @@ -385,15 +393,13 @@ 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 + // Load the network from the database for the local node. + n, err := network.LoadByName(d.State(), projectName, req.Name) + if err != nil { + return err } - err = doNetworksCreate(d, projectName, nodeReq, clientType) + err = doNetworksCreate(d, n, clientType) if err != nil { return err } @@ -406,18 +412,31 @@ func networksPostCluster(d *Daemon, projectName string, req api.NetworksPost, cl return err } - nodeReq := req + // Create fresh request based on existing network to send to node. + nodeReq := api.NetworksPost{ + NetworkPut: api.NetworkPut{ + Config: n.Config(), + Description: n.Description(), + }, + Name: n.Name(), + Type: n.Type(), + } + + // Remove node-specific config keys. + for _, key := range db.NodeSpecificNetworkConfig { + delete(nodeReq.Config, key) + } // Merge node specific config items into global config. - for key, value := range configs[server.Environment.ServerName] { + for key, value := range nodeConfigs[server.Environment.ServerName] { nodeReq.Config[key] = value } - err = client.UseProject(projectName).CreateNetwork(nodeReq) + err = client.UseProject(n.Project()).CreateNetwork(nodeReq) if err != nil { return err } - logger.Debug("Created network on cluster member", log.Ctx{"project": projectName, "network": req.Name, "member": server.Environment.ServerName}) + logger.Debug("Created network on cluster member", log.Ctx{"project": n.Project(), "network": n.Name(), "member": server.Environment.ServerName, "config": nodeReq.Config}) return nil }) From 880a616d29b8d8570f2fd824ce7003d75e884451 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Fri, 11 Dec 2020 17:23:37 +0000 Subject: [PATCH 7/7] lxd/networks: Updates doNetworksCreate to accept a Network rather than load its own This is useful as it can be preloaded and used in other places to avoid duplicate queries. Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/networks.go | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/lxd/networks.go b/lxd/networks.go index fef87b60e8..690c66f170 100644 --- a/lxd/networks.go +++ b/lxd/networks.go @@ -458,24 +458,18 @@ func networksPostCluster(d *Daemon, projectName string, req api.NetworksPost, cl // 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, projectName string, req api.NetworksPost, clientType request.ClientType) error { +func doNetworksCreate(d *Daemon, n network.Network, clientType request.ClientType) error { revert := revert.New() defer revert.Fail() - // Start the network. - n, err := network.LoadByName(d.State(), projectName, req.Name) - if err != nil { - return err - } - // Validate so that when run on a cluster node the full config (including node specific config) is checked. - err = n.Validate(n.Config()) + err := n.Validate(n.Config()) if err != nil { return err } if n.LocalStatus() == api.NetworkStatusCreated { - logger.Debug("Skipping network create as already created locally", log.Ctx{"project": projectName, "network": n.Name()}) + logger.Debug("Skipping network create as already created locally", log.Ctx{"project": n.Project(), "network": n.Name()}) return nil } @@ -503,7 +497,7 @@ func doNetworksCreate(d *Daemon, projectName string, req api.NetworksPost, clien if err != nil { return err } - logger.Debug("Marked network local status as created", log.Ctx{"project": projectName, "network": req.Name}) + logger.Debug("Marked network local status as created", log.Ctx{"project": n.Project(), "network": n.Name()}) revert.Success() return nil
_______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel