[lxc-devel] [lxd/master] Network: Don't require --target arg when adding networks in a cluster that don't require per-node config

tomponline on Github lxc-bot at linuxcontainers.org
Mon Sep 21 14:32:05 UTC 2020


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 1008 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20200921/f3a24412/attachment-0001.bin>
-------------- next part --------------
From cbcd0da2fa9b5fdc43d114c97138d45e7f8a91c5 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 21 Sep 2020 10:34:19 +0100
Subject: [PATCH 01/22] lxd/network/network/interface: Adds Type interface and
 moves non-DB depedent functions into it

Embeds Type interface into existing Network interface.

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

diff --git a/lxd/network/network_interface.go b/lxd/network/network_interface.go
index 2e3cefd4a1..1a095feb71 100644
--- a/lxd/network/network_interface.go
+++ b/lxd/network/network_interface.go
@@ -9,19 +9,25 @@ import (
 	"github.com/lxc/lxd/shared/api"
 )
 
-// Network represents a LXD network.
+// Type represents an LXD network driver type.
+type Type interface {
+	fillConfig(config map[string]string) error
+	Info() Info
+	ValidateName(name string) error
+	Type() string
+}
+
+// Network represents an instantiated LXD network.
 type Network interface {
+	Type
+
 	// Load.
 	init(state *state.State, id int64, projectName string, name string, netType string, description string, config map[string]string, status string)
-	fillConfig(config map[string]string) error
-	Info() Info
 
 	// Config.
-	ValidateName(name string) error
 	Validate(config map[string]string) error
 	ID() int64
 	Name() string
-	Type() string
 	Description() string
 	Status() string
 	Config() map[string]string

From 4454e4c9a3fd5c9b04dacfdad6b4d2b6fa811410 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 21 Sep 2020 10:35:12 +0100
Subject: [PATCH 02/22] lxd/network/network/load: Adds LoadByType function and
 removes ValidateNameAndProject function

As we add more driver type functionality that needs to occur before DB records are created, it makes sense to move these functions into their own interface, rather than relying on ever more helper functions at the package level.

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

diff --git a/lxd/network/network_load.go b/lxd/network/network_load.go
index d3fe2cd50f..90f10c3649 100644
--- a/lxd/network/network_load.go
+++ b/lxd/network/network_load.go
@@ -1,8 +1,6 @@
 package network
 
 import (
-	"fmt"
-
 	"github.com/pkg/errors"
 
 	"github.com/lxc/lxd/lxd/project"
@@ -17,7 +15,19 @@ var drivers = map[string]func() Network{
 	"ovn":     func() Network { return &ovn{} },
 }
 
-// LoadByName loads the network info from the database by name.
+// LoadByType loads a network by driver type.
+func LoadByType(driverType string) (Type, error) {
+	driverFunc, ok := drivers[driverType]
+	if !ok {
+		return nil, ErrUnknownDriver
+	}
+
+	n := driverFunc()
+
+	return n, nil
+}
+
+// LoadByName loads an instantiated network from the database by name.
 func LoadByName(s *state.State, project string, name string) (Network, error) {
 	id, netInfo, err := s.Cluster.GetNetworkInAnyState(project, name)
 	if err != nil {
@@ -35,28 +45,6 @@ func LoadByName(s *state.State, project string, name string) (Network, error) {
 	return n, nil
 }
 
-// ValidateNameAndProject validates the supplied network name and project support for the specified network type.
-func ValidateNameAndProject(name string, networkProjectName string, netType string) error {
-	driverFunc, ok := drivers[netType]
-	if !ok {
-		return ErrUnknownDriver
-	}
-
-	n := driverFunc()
-	n.init(nil, 0, networkProjectName, name, netType, "", nil, "Unknown")
-
-	err := n.ValidateName(name)
-	if err != nil {
-		return errors.Wrapf(err, "Network name invalid")
-	}
-
-	if networkProjectName != project.Default && !n.Info().Projects {
-		return fmt.Errorf("Network type does not support non-default projects")
-	}
-
-	return nil
-}
-
 // Validate validates the supplied network name and configuration for the specified network type.
 func Validate(name string, netType string, config map[string]string) error {
 	driverFunc, ok := drivers[netType]

From 335ba9bdb20c22f0b278a2b3a05bfa15772bc652 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 21 Sep 2020 10:36:37 +0100
Subject: [PATCH 03/22] lxd/main/init/interactive: netType.ValidateName usage

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

diff --git a/lxd/main_init_interactive.go b/lxd/main_init_interactive.go
index 95679380b6..5fa8f755a6 100644
--- a/lxd/main_init_interactive.go
+++ b/lxd/main_init_interactive.go
@@ -365,7 +365,14 @@ func (c *cmdInit) askNetworking(config *cmdInitData, d lxd.InstanceServer) error
 		net.Project = project.Default
 
 		// Network name
-		net.Name = cli.AskString("What should the new bridge be called? [default=lxdbr0]: ", "lxdbr0", func(netName string) error { return network.ValidateNameAndProject(netName, project.Default, "bridge") })
+		net.Name = cli.AskString("What should the new bridge be called? [default=lxdbr0]: ", "lxdbr0", func(netName string) error {
+			netType, err := network.LoadByType("bridge")
+			if err != nil {
+				return err
+			}
+
+			return netType.ValidateName(netName)
+		})
 		_, _, err := d.GetNetwork(net.Name)
 		if err == nil {
 			fmt.Printf("The requested network bridge \"%s\" already exists. Please choose another name.\n", net.Name)

From ceb500dd55671f4ec71fdb5cbdae82f5b3daa03d Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 21 Sep 2020 10:37:11 +0100
Subject: [PATCH 04/22] lxd/networks: Switch to network type validation in
 networksPost

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

diff --git a/lxd/networks.go b/lxd/networks.go
index a294227cc0..a5d07c8566 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -151,11 +151,21 @@ func networksPost(d *Daemon, r *http.Request) response.Response {
 		req.Config = map[string]string{}
 	}
 
-	err = network.ValidateNameAndProject(req.Name, projectName, req.Type)
+	netType, err := network.LoadByType(req.Type)
 	if err != nil {
 		return response.BadRequest(err)
 	}
 
+	err = netType.ValidateName(req.Name)
+	if err != nil {
+		return response.BadRequest(err)
+	}
+
+	netTypeInfo := netType.Info()
+	if projectName != project.Default && !netTypeInfo.Projects {
+		return response.BadRequest(fmt.Errorf("Network type does not support non-default projects"))
+	}
+
 	// Check if project has limits.network and if so check we are allowed to create another network.
 	if projectName != project.Default && projectConfig != nil && projectConfig["limits.networks"] != "" {
 		networksLimit, err := strconv.Atoi(projectConfig["limits.networks"])

From e5f92170652d5e9349732da43b5a4be23997e190 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 21 Sep 2020 10:37:38 +0100
Subject: [PATCH 05/22] lxd/networks: Use ValidateName function on loaded DB
 network in networkPost

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

diff --git a/lxd/networks.go b/lxd/networks.go
index a5d07c8566..a33b058eba 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -646,7 +646,7 @@ func networkPost(d *Daemon, r *http.Request) response.Response {
 		return response.BadRequest(fmt.Errorf("New network name not provided"))
 	}
 
-	err = network.ValidateNameAndProject(req.Name, projectName, n.Type())
+	err = n.ValidateName(req.Name)
 	if err != nil {
 		return response.BadRequest(err)
 	}

From 77b5b0bded45befed01c10891a81c50e9498e1a3 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 21 Sep 2020 10:57:19 +0100
Subject: [PATCH 06/22] lxd/network/network/interface: Exports FillConfig

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

diff --git a/lxd/network/network_interface.go b/lxd/network/network_interface.go
index 1a095feb71..0dc28b4184 100644
--- a/lxd/network/network_interface.go
+++ b/lxd/network/network_interface.go
@@ -11,7 +11,7 @@ import (
 
 // Type represents an LXD network driver type.
 type Type interface {
-	fillConfig(config map[string]string) error
+	FillConfig(config map[string]string) error
 	Info() Info
 	ValidateName(name string) error
 	Type() string

From 2b77135298ef1f7cd6938171b24af452a473a040 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 21 Sep 2020 10:57:40 +0100
Subject: [PATCH 07/22] lxd/network/network/load: Removes FillConfig function

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

diff --git a/lxd/network/network_load.go b/lxd/network/network_load.go
index 90f10c3649..862ba2831b 100644
--- a/lxd/network/network_load.go
+++ b/lxd/network/network_load.go
@@ -5,7 +5,6 @@ import (
 
 	"github.com/lxc/lxd/lxd/project"
 	"github.com/lxc/lxd/lxd/state"
-	"github.com/lxc/lxd/shared/api"
 )
 
 var drivers = map[string]func() Network{
@@ -62,21 +61,3 @@ func Validate(name string, netType string, config map[string]string) error {
 
 	return n.Validate(config)
 }
-
-// FillConfig populates the supplied api.NetworkPost with automatically populated values.
-func FillConfig(req *api.NetworksPost) error {
-	driverFunc, ok := drivers[req.Type]
-	if !ok {
-		return ErrUnknownDriver
-	}
-
-	n := driverFunc()
-	n.init(nil, 0, project.Default, req.Name, req.Type, req.Description, req.Config, "Unknown")
-
-	err := n.fillConfig(req.Config)
-	if err != nil {
-		return err
-	}
-
-	return nil
-}

From b7e848b5b22851f05c0a405858bfa5636fefb65e Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 21 Sep 2020 10:58:14 +0100
Subject: [PATCH 08/22] lxd/networks: netType.FillConfig usage

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

diff --git a/lxd/networks.go b/lxd/networks.go
index a33b058eba..cdcd5ce2c7 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -246,7 +246,7 @@ func networksPost(d *Daemon, r *http.Request) response.Response {
 	}
 
 	if count > 1 {
-		err = networksPostCluster(d, projectName, req, clientType)
+		err = networksPostCluster(d, projectName, req, clientType, netType)
 		if err != nil {
 			return response.SmartError(err)
 		}
@@ -255,7 +255,7 @@ func networksPost(d *Daemon, r *http.Request) response.Response {
 	}
 
 	// Non-clustered network creation.
-	err = network.FillConfig(&req)
+	err = netType.FillConfig(req.Config)
 	if err != nil {
 		return response.SmartError(err)
 	}
@@ -291,7 +291,7 @@ func networksPost(d *Daemon, r *http.Request) response.Response {
 	return resp
 }
 
-func networksPostCluster(d *Daemon, projectName string, req api.NetworksPost, clientType cluster.ClientType) error {
+func networksPostCluster(d *Daemon, projectName string, req api.NetworksPost, clientType cluster.ClientType, netType network.Type) error {
 	// Check that no node-specific config key has been defined.
 	for key := range req.Config {
 		if shared.StringInSlice(key, db.NodeSpecificNetworkConfig) {
@@ -311,7 +311,7 @@ func networksPostCluster(d *Daemon, projectName string, req api.NetworksPost, cl
 	}
 
 	// Add default values.
-	err = network.FillConfig(&req)
+	err = netType.FillConfig(req.Config)
 	if err != nil {
 		return err
 	}

From e3e07697081285cf4350a03012231071aad95483 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 21 Sep 2020 10:58:43 +0100
Subject: [PATCH 09/22] lxd/network/driver/common: Exports FillConfig

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

diff --git a/lxd/network/driver_common.go b/lxd/network/driver_common.go
index 23e9f72d8b..6a4eda396d 100644
--- a/lxd/network/driver_common.go
+++ b/lxd/network/driver_common.go
@@ -44,15 +44,14 @@ func (n *common) init(state *state.State, id int64, projectName string, name str
 	n.id = id
 	n.project = projectName
 	n.name = name
-	n.netType = netType
 	n.config = config
 	n.state = state
 	n.description = description
 	n.status = status
 }
 
-// fillConfig fills requested config with any default values, by default this is a no-op.
-func (n *common) fillConfig(config map[string]string) error {
+// FillConfig fills requested config with any default values, by default this is a no-op.
+func (n *common) FillConfig(config map[string]string) error {
 	return nil
 }
 

From 2f642388b57522921a7317d36b671e69b98e3ea0 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 21 Sep 2020 11:00:04 +0100
Subject: [PATCH 10/22] lxd/network/driver/bridge: FillConfig usage

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

diff --git a/lxd/network/driver_bridge.go b/lxd/network/driver_bridge.go
index 42c10c9814..5cc54a38f0 100644
--- a/lxd/network/driver_bridge.go
+++ b/lxd/network/driver_bridge.go
@@ -71,8 +71,8 @@ func (n *bridge) checkClusterWideMACSafe(config map[string]string) error {
 	return nil
 }
 
-// fillConfig fills requested config with any default values.
-func (n *bridge) fillConfig(config map[string]string) error {
+// FillConfig fills requested config with any default values.
+func (n *bridge) FillConfig(config map[string]string) error {
 	// Set some default values where needed.
 	if config["bridge.mode"] == "fan" {
 		if config["fan.underlay_subnet"] == "" {
@@ -1504,7 +1504,7 @@ func (n *bridge) Update(newNetwork api.NetworkPut, targetNode string, clientType
 	n.logger.Debug("Update", log.Ctx{"clientType": clientType, "newNetwork": newNetwork})
 
 	// Populate default values if they are missing.
-	err := n.fillConfig(newNetwork.Config)
+	err := n.FillConfig(newNetwork.Config)
 	if err != nil {
 		return err
 	}

From 7f3b012ba2e67be527e890a64814f5a25f064c84 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 21 Sep 2020 11:00:27 +0100
Subject: [PATCH 11/22] lxd/network/driver/ovn: FillConfig usage

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

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index d136d47c46..d79618d6d5 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -685,8 +685,8 @@ func (n *ovn) deleteParentPortBridge(parentNet Network) error {
 	return nil
 }
 
-// fillConfig fills requested config with any default values.
-func (n *ovn) fillConfig(config map[string]string) error {
+// FillConfig fills requested config with any default values.
+func (n *ovn) FillConfig(config map[string]string) error {
 	if config["ipv4.address"] == "" {
 		config["ipv4.address"] = "auto"
 	}
@@ -1209,7 +1209,7 @@ func (n *ovn) Update(newNetwork api.NetworkPut, targetNode string, clientType cl
 	n.logger.Debug("Update", log.Ctx{"clientType": clientType, "newNetwork": newNetwork})
 
 	// Populate default values if they are missing.
-	err := n.fillConfig(newNetwork.Config)
+	err := n.FillConfig(newNetwork.Config)
 	if err != nil {
 		return err
 	}

From a1a80fd6e884fc1fe2a629932745ec1ed68a74e6 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 21 Sep 2020 10:59:06 +0100
Subject: [PATCH 12/22] lxd/network/driver/common: Removes common Type() and
 netType

Type() should be implemented by each driver.

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

diff --git a/lxd/network/driver_common.go b/lxd/network/driver_common.go
index 6a4eda396d..dae25bbaa1 100644
--- a/lxd/network/driver_common.go
+++ b/lxd/network/driver_common.go
@@ -32,7 +32,6 @@ type common struct {
 	id          int64
 	project     string
 	name        string
-	netType     string
 	description string
 	config      map[string]string
 	status      string
@@ -124,11 +123,6 @@ func (n *common) Status() string {
 	return n.status
 }
 
-// Type returns the network type.
-func (n *common) Type() string {
-	return n.netType
-}
-
 // Config returns the network config.
 func (n *common) Config() map[string]string {
 	return n.config
@@ -205,7 +199,8 @@ func (n *common) DHCPv6Ranges() []shared.IPRange {
 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.project, n.name, n.netType, applyNetwork.Description, applyNetwork.Config, n.status)
+	n.description = applyNetwork.Description
+	n.config = applyNetwork.Config
 
 	// If this update isn't coming via a cluster notification itself, then notify all nodes of change and then
 	// update the database.
@@ -316,7 +311,7 @@ func (n *common) rename(newName string) error {
 	}
 
 	// Reinitialise internal name variable and logger context with new name.
-	n.init(n.state, n.id, n.project, newName, n.netType, n.description, n.config, n.status)
+	n.name = newName
 
 	return nil
 }

From adadc6640b27bc7c0947b5435307f430d7487d01 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 21 Sep 2020 11:00:56 +0100
Subject: [PATCH 13/22] lxd/network: Adds Type() to each driver

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

diff --git a/lxd/network/driver_bridge.go b/lxd/network/driver_bridge.go
index 5cc54a38f0..100731cd61 100644
--- a/lxd/network/driver_bridge.go
+++ b/lxd/network/driver_bridge.go
@@ -48,6 +48,11 @@ type bridge struct {
 	common
 }
 
+// Type returns the network type.
+func (n *bridge) Type() string {
+	return "bridge"
+}
+
 // checkClusterWideMACSafe returns whether it is safe to use the same MAC address for the bridge interface on all
 // cluster nodes. It is not suitable to use a static MAC address when "bridge.external_interfaces" is non-empty an
 // the bridge interface has no IPv4 or IPv6 address set. This is because in a clustered environment the same bridge
diff --git a/lxd/network/driver_macvlan.go b/lxd/network/driver_macvlan.go
index 7593eb82db..8b4369c20f 100644
--- a/lxd/network/driver_macvlan.go
+++ b/lxd/network/driver_macvlan.go
@@ -15,6 +15,11 @@ type macvlan struct {
 	common
 }
 
+// Type returns the network type.
+func (n *macvlan) Type() string {
+	return "macvlan"
+}
+
 // Validate network config.
 func (n *macvlan) Validate(config map[string]string) error {
 	rules := map[string]func(value string) error{
diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index d79618d6d5..7e09d6a0b2 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -63,6 +63,11 @@ type ovn struct {
 	common
 }
 
+// Type returns the network type.
+func (n *ovn) Type() string {
+	return "ovn"
+}
+
 // Config returns the network driver info.
 func (n *ovn) Info() Info {
 	return Info{
diff --git a/lxd/network/driver_sriov.go b/lxd/network/driver_sriov.go
index 6bcbec7557..1ed0df6cdd 100644
--- a/lxd/network/driver_sriov.go
+++ b/lxd/network/driver_sriov.go
@@ -15,6 +15,11 @@ type sriov struct {
 	common
 }
 
+// Type returns the network type.
+func (n *sriov) Type() string {
+	return "sriov"
+}
+
 // Validate network config.
 func (n *sriov) Validate(config map[string]string) error {
 	rules := map[string]func(value string) error{

From 861ae0d937cb927b0a1a2911a837a228583485dd Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 21 Sep 2020 15:19:18 +0100
Subject: [PATCH 14/22] lxd/db/errors: Updates ErrAlreadyDefined text to be
 generic

As is used in non-instance contexts.

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

diff --git a/lxd/db/errors.go b/lxd/db/errors.go
index cdd4a2eee2..c1f046a7dc 100644
--- a/lxd/db/errors.go
+++ b/lxd/db/errors.go
@@ -7,7 +7,7 @@ import (
 var (
 	// ErrAlreadyDefined hapens when the given entry already exists,
 	// for example a container.
-	ErrAlreadyDefined = fmt.Errorf("The instance/snapshot already exists")
+	ErrAlreadyDefined = fmt.Errorf("The record already exists")
 
 	// ErrNoSuchObject is in the case of joins (and probably other) queries,
 	// we don't get back sql.ErrNoRows when no rows are returned, even though we do

From 7a06fbc31b0a7a226a6fcf6c32c734a9b4891c07 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 21 Sep 2020 15:19:46 +0100
Subject: [PATCH 15/22] lxd/network/network/interface: Adds DBType function

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

diff --git a/lxd/network/network_interface.go b/lxd/network/network_interface.go
index 0dc28b4184..b9ba1e89d1 100644
--- a/lxd/network/network_interface.go
+++ b/lxd/network/network_interface.go
@@ -4,6 +4,7 @@ import (
 	"net"
 
 	"github.com/lxc/lxd/lxd/cluster"
+	"github.com/lxc/lxd/lxd/db"
 	"github.com/lxc/lxd/lxd/state"
 	"github.com/lxc/lxd/shared"
 	"github.com/lxc/lxd/shared/api"
@@ -15,6 +16,7 @@ type Type interface {
 	Info() Info
 	ValidateName(name string) error
 	Type() string
+	DBType() db.NetworkType
 }
 
 // Network represents an instantiated LXD network.

From cb5a4da6a6bd2ab19ce3369aeca8c2c75be0c8a1 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 21 Sep 2020 15:20:38 +0100
Subject: [PATCH 16/22] lxd/network/driver: Implements DBType()

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

diff --git a/lxd/network/driver_bridge.go b/lxd/network/driver_bridge.go
index 100731cd61..cce5e1d245 100644
--- a/lxd/network/driver_bridge.go
+++ b/lxd/network/driver_bridge.go
@@ -21,6 +21,7 @@ import (
 	"github.com/lxc/lxd/lxd/apparmor"
 	"github.com/lxc/lxd/lxd/cluster"
 	"github.com/lxc/lxd/lxd/daemon"
+	"github.com/lxc/lxd/lxd/db"
 	"github.com/lxc/lxd/lxd/dnsmasq"
 	"github.com/lxc/lxd/lxd/dnsmasq/dhcpalloc"
 	"github.com/lxc/lxd/lxd/network/openvswitch"
@@ -53,6 +54,11 @@ func (n *bridge) Type() string {
 	return "bridge"
 }
 
+// DBType returns the network type DB ID.
+func (n *bridge) DBType() db.NetworkType {
+	return db.NetworkTypeBridge
+}
+
 // checkClusterWideMACSafe returns whether it is safe to use the same MAC address for the bridge interface on all
 // cluster nodes. It is not suitable to use a static MAC address when "bridge.external_interfaces" is non-empty an
 // the bridge interface has no IPv4 or IPv6 address set. This is because in a clustered environment the same bridge
diff --git a/lxd/network/driver_macvlan.go b/lxd/network/driver_macvlan.go
index 8b4369c20f..8f82680e35 100644
--- a/lxd/network/driver_macvlan.go
+++ b/lxd/network/driver_macvlan.go
@@ -4,6 +4,7 @@ import (
 	"fmt"
 
 	"github.com/lxc/lxd/lxd/cluster"
+	"github.com/lxc/lxd/lxd/db"
 	"github.com/lxc/lxd/lxd/revert"
 	"github.com/lxc/lxd/shared/api"
 	log "github.com/lxc/lxd/shared/log15"
@@ -20,6 +21,11 @@ func (n *macvlan) Type() string {
 	return "macvlan"
 }
 
+// DBType returns the network type DB ID.
+func (n *macvlan) DBType() db.NetworkType {
+	return db.NetworkTypeMacvlan
+}
+
 // Validate network config.
 func (n *macvlan) Validate(config map[string]string) error {
 	rules := map[string]func(value string) error{
diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index 7e09d6a0b2..fa68cf4e6d 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -68,6 +68,11 @@ func (n *ovn) Type() string {
 	return "ovn"
 }
 
+// DBType returns the network type DB ID.
+func (n *ovn) DBType() db.NetworkType {
+	return db.NetworkTypeOVN
+}
+
 // Config returns the network driver info.
 func (n *ovn) Info() Info {
 	return Info{
diff --git a/lxd/network/driver_sriov.go b/lxd/network/driver_sriov.go
index 1ed0df6cdd..698242778f 100644
--- a/lxd/network/driver_sriov.go
+++ b/lxd/network/driver_sriov.go
@@ -4,6 +4,7 @@ import (
 	"fmt"
 
 	"github.com/lxc/lxd/lxd/cluster"
+	"github.com/lxc/lxd/lxd/db"
 	"github.com/lxc/lxd/lxd/revert"
 	"github.com/lxc/lxd/shared/api"
 	log "github.com/lxc/lxd/shared/log15"
@@ -20,6 +21,11 @@ func (n *sriov) Type() string {
 	return "sriov"
 }
 
+// DBType returns the network type DB ID.
+func (n *sriov) DBType() db.NetworkType {
+	return db.NetworkTypeSriov
+}
+
 // Validate network config.
 func (n *sriov) Validate(config map[string]string) error {
 	rules := map[string]func(value string) error{

From a8c46bcfb5690b4b147290fda2e7e4bd690d3261 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 21 Sep 2020 15:21:00 +0100
Subject: [PATCH 17/22] lxd/network/driver: Adds NodeSpecificConfig Info var

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/network/driver_common.go | 6 ++++--
 lxd/network/driver_ovn.go    | 3 ++-
 2 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/lxd/network/driver_common.go b/lxd/network/driver_common.go
index dae25bbaa1..3c422c5a1a 100644
--- a/lxd/network/driver_common.go
+++ b/lxd/network/driver_common.go
@@ -22,7 +22,8 @@ import (
 
 // Info represents information about a network driver.
 type Info struct {
-	Projects bool // Indicates if driver can be used in network enabled projects.
+	Projects           bool // Indicates if driver can be used in network enabled projects.
+	NodeSpecificConfig bool // Whether driver has cluster node specific config as a prerequisite for creation.
 }
 
 // common represents a generic LXD network.
@@ -131,7 +132,8 @@ func (n *common) Config() map[string]string {
 // Config returns the common network driver info.
 func (n *common) Info() Info {
 	return Info{
-		Projects: false,
+		Projects:           false,
+		NodeSpecificConfig: true,
 	}
 }
 
diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index fa68cf4e6d..0f62cc7d4c 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -76,7 +76,8 @@ func (n *ovn) DBType() db.NetworkType {
 // Config returns the network driver info.
 func (n *ovn) Info() Info {
 	return Info{
-		Projects: true,
+		Projects:           true,
+		NodeSpecificConfig: false,
 	}
 }
 

From d0ae93e802f66b9e7df9c53cd2a87f562b6d95a9 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 21 Sep 2020 15:24:23 +0100
Subject: [PATCH 18/22] lxd/networks: netType.DBType usage in networksPost

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

diff --git a/lxd/networks.go b/lxd/networks.go
index cdcd5ce2c7..e1b69e1fd4 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -186,21 +186,6 @@ func networksPost(d *Daemon, r *http.Request) response.Response {
 		}
 	}
 
-	// Convert requested network type to DB type code.
-	var dbNetType db.NetworkType
-	switch req.Type {
-	case "bridge":
-		dbNetType = db.NetworkTypeBridge
-	case "macvlan":
-		dbNetType = db.NetworkTypeMacvlan
-	case "sriov":
-		dbNetType = db.NetworkTypeSriov
-	case "ovn":
-		dbNetType = db.NetworkTypeOVN
-	default:
-		return response.BadRequest(fmt.Errorf("Unrecognised network type"))
-	}
-
 	url := fmt.Sprintf("/%s/networks/%s", version.APIVersion, req.Name)
 	resp := response.SyncResponseLocation(true, nil, url)
 
@@ -218,6 +203,10 @@ func networksPost(d *Daemon, r *http.Request) response.Response {
 
 	targetNode := queryParam(r, "target")
 	if targetNode != "" {
+		if !netTypeInfo.NodeSpecificConfig {
+			return response.BadRequest(fmt.Errorf("Network type %q does not support node specific config", netType.Type()))
+		}
+
 		// A targetNode was specified, let's just define the node's network without actually creating it.
 		// Check that only NodeSpecificNetworkConfig keys are specified.
 		for key := range req.Config {
@@ -227,7 +216,7 @@ func networksPost(d *Daemon, r *http.Request) response.Response {
 		}
 
 		err = d.cluster.Transaction(func(tx *db.ClusterTx) error {
-			return tx.CreatePendingNetwork(targetNode, projectName, req.Name, dbNetType, req.Config)
+			return tx.CreatePendingNetwork(targetNode, projectName, req.Name, netType.DBType(), req.Config)
 		})
 		if err != nil {
 			if err == db.ErrAlreadyDefined {
@@ -269,15 +258,20 @@ func networksPost(d *Daemon, r *http.Request) response.Response {
 		return response.BadRequest(fmt.Errorf("The network already exists"))
 	}
 
+	// Populate default config.
+	err = netType.FillConfig(req.Config)
+	if err != nil {
+		return response.SmartError(err)
+	}
+
 	revert := revert.New()
 	defer revert.Fail()
 
 	// Create the database entry.
-	_, err = d.cluster.CreateNetwork(projectName, req.Name, req.Description, dbNetType, req.Config)
+	_, err = d.cluster.CreateNetwork(projectName, req.Name, req.Description, netType.DBType(), req.Config)
 	if err != nil {
 		return response.SmartError(errors.Wrapf(err, "Error inserting %q into database", req.Name))
 	}
-
 	revert.Add(func() {
 		d.cluster.DeleteNetwork(projectName, req.Name)
 	})

From c1da0f64ee2bfa0e1b921ee86b6c4e5973e14e52 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 21 Sep 2020 15:25:54 +0100
Subject: [PATCH 19/22] lxd/networks: Create pending network node entries when
 network driver doesn't support per node config in networksPost

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

diff --git a/lxd/networks.go b/lxd/networks.go
index e1b69e1fd4..10903bcbe2 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -201,6 +201,9 @@ func networksPost(d *Daemon, r *http.Request) response.Response {
 		return resp
 	}
 
+	revert := revert.New()
+	defer revert.Fail()
+
 	targetNode := queryParam(r, "target")
 	if targetNode != "" {
 		if !netTypeInfo.NodeSpecificConfig {
@@ -226,6 +229,31 @@ func networksPost(d *Daemon, r *http.Request) response.Response {
 		}
 
 		return resp
+	} else if !netTypeInfo.NodeSpecificConfig && clientType != cluster.ClientTypeJoiner {
+		// Simulate adding pending node network config when the driver doesn't support per-node config.
+		revert.Add(func() {
+			d.cluster.DeleteNetwork(projectName, req.Name)
+		})
+
+		// Create pending entry for each node.
+		err = d.cluster.Transaction(func(tx *db.ClusterTx) error {
+			nodes, err := tx.GetNodes()
+			if err != nil {
+				return err
+			}
+
+			for _, node := range nodes {
+				err = tx.CreatePendingNetwork(node.Name, projectName, req.Name, netType.DBType(), req.Config)
+				if err != nil {
+					return errors.Wrapf(err, "Failed creating pending network for node %q", node.Name)
+				}
+			}
+
+			return nil
+		})
+		if err != nil {
+			return response.SmartError(err)
+		}
 	}
 
 	// Check if we're clustered.
@@ -240,14 +268,11 @@ func networksPost(d *Daemon, r *http.Request) response.Response {
 			return response.SmartError(err)
 		}
 
+		revert.Success()
 		return resp
 	}
 
 	// Non-clustered network creation.
-	err = netType.FillConfig(req.Config)
-	if err != nil {
-		return response.SmartError(err)
-	}
 
 	networks, err := d.cluster.GetNetworks(projectName)
 	if err != nil {
@@ -264,9 +289,6 @@ func networksPost(d *Daemon, r *http.Request) response.Response {
 		return response.SmartError(err)
 	}
 
-	revert := revert.New()
-	defer revert.Fail()
-
 	// Create the database entry.
 	_, err = d.cluster.CreateNetwork(projectName, req.Name, req.Description, netType.DBType(), req.Config)
 	if err != nil {

From 23f7b060e67e6ff36d2f078aeb664d4df6fcb54c Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 21 Sep 2020 15:26:58 +0100
Subject: [PATCH 20/22] lxd/networks: Comments in networksPostCluster

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

diff --git a/lxd/networks.go b/lxd/networks.go
index 10903bcbe2..1269f2a95a 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -308,7 +308,7 @@ func networksPost(d *Daemon, r *http.Request) response.Response {
 }
 
 func networksPostCluster(d *Daemon, projectName string, req api.NetworksPost, clientType cluster.ClientType, netType network.Type) error {
-	// Check that no node-specific config key has been defined.
+	// Check that no node-specific config key has been supplied in request.
 	for key := range req.Config {
 		if shared.StringInSlice(key, db.NodeSpecificNetworkConfig) {
 			return fmt.Errorf("Config key %q is node-specific", key)
@@ -368,6 +368,8 @@ func networksPostCluster(d *Daemon, projectName string, req api.NetworksPost, cl
 
 	// 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
 	}

From ea611ba00aefe90264aecf9a165e956d6a625b71 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 21 Sep 2020 15:27:10 +0100
Subject: [PATCH 21/22] lxd/networks: Comments in networkGet

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

diff --git a/lxd/networks.go b/lxd/networks.go
index 1269f2a95a..87ed6ccd0b 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -483,8 +483,7 @@ func networkGet(d *Daemon, r *http.Request) response.Response {
 		return response.SmartError(err)
 	}
 
-	// If no target node is specified and the daemon is clustered, we omit
-	// the node-specific fields.
+	// If no target node is specified and the daemon is clustered, we omit the node-specific fields.
 	if targetNode == "" && clustered {
 		for _, key := range db.NodeSpecificNetworkConfig {
 			delete(n.Config, key)

From df23ec594d2c743657b702634b8f3f63a935f96a Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 21 Sep 2020 15:27:24 +0100
Subject: [PATCH 22/22] lxd/networks: Start parent networks before dependents
 in networkStartup

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

diff --git a/lxd/networks.go b/lxd/networks.go
index 87ed6ccd0b..e53e5bf088 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -1020,7 +1020,12 @@ func networkStartup(s *state.State) error {
 		return errors.Wrapf(err, "Failed to load projects")
 	}
 
+	// Record of networks that need to be started later keyed on project name.
+	deferredNetworks := make(map[string][]network.Network)
+
 	for _, projectName := range projectNames {
+		deferredNetworks[projectName] = make([]network.Network, 0)
+
 		// Get a list of managed networks.
 		networks, err := s.Cluster.GetNonPendingNetworks(projectName)
 		if err != nil {
@@ -1034,13 +1039,20 @@ func networkStartup(s *state.State) error {
 				return errors.Wrapf(err, "Failed to load network %q in project %q", name, projectName)
 			}
 
-			err = n.Validate(n.Config())
+			netConfig := n.Config()
+			err = n.Validate(netConfig)
 			if err != nil {
 				// Don't cause LXD to fail to start entirely on network start up failure.
 				logger.Error("Failed to validate network", log.Ctx{"err": err, "project": projectName, "name": name})
 				continue
 			}
 
+			// Defer network start until after non-dependent networks.
+			if netConfig["network"] != "" {
+				deferredNetworks[projectName] = append(deferredNetworks[projectName], n)
+				continue
+			}
+
 			err = n.Start()
 			if err != nil {
 				// Don't cause LXD to fail to start entirely on network start up failure.
@@ -1050,6 +1062,18 @@ func networkStartup(s *state.State) error {
 		}
 	}
 
+	// Bring up deferred networks after non-dependent networks have been started.
+	for projectName, networks := range deferredNetworks {
+		for _, n := range networks {
+			err = n.Start()
+			if err != nil {
+				// Don't cause LXD to fail to start entirely on network start up failure.
+				logger.Error("Failed to bring up network", log.Ctx{"err": err, "project": projectName, "name": n.Name()})
+				continue
+			}
+		}
+	}
+
 	return nil
 }
 


More information about the lxc-devel mailing list