[lxc-devel] [lxd/master] Network: OVN uplink terminology

tomponline on Github lxc-bot at linuxcontainers.org
Tue Oct 13 12:43:17 UTC 2020


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 489 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20201013/e65e1ae1/attachment-0001.bin>
-------------- next part --------------
From 5d8ced58e9b060ef3638237f3b6ffeb150c65c23 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Fri, 9 Oct 2020 09:38:36 +0100
Subject: [PATCH 1/8] shared/validate/validate: Removes inaccurate comments
 about optional values

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

diff --git a/shared/validate/validate.go b/shared/validate/validate.go
index 10067d8c26..92939c4e39 100644
--- a/shared/validate/validate.go
+++ b/shared/validate/validate.go
@@ -161,7 +161,7 @@ func IsNetworkMAC(value string) error {
 	return nil
 }
 
-// IsNetworkAddress validates an IP (v4 or v6) address string. If string is empty, returns valid.
+// IsNetworkAddress validates an IP (v4 or v6) address string.
 func IsNetworkAddress(value string) error {
 	ip := net.ParseIP(value)
 	if ip == nil {
@@ -184,7 +184,7 @@ func IsNetworkAddressList(value string) error {
 	return nil
 }
 
-// IsNetworkV4 validates an IPv4 CIDR string. If string is empty, returns valid.
+// IsNetworkV4 validates an IPv4 CIDR string.
 func IsNetworkV4(value string) error {
 	ip, subnet, err := net.ParseCIDR(value)
 	if err != nil {
@@ -202,7 +202,7 @@ func IsNetworkV4(value string) error {
 	return nil
 }
 
-// IsNetworkAddressV4 validates an IPv4 addresss string. If string is empty, returns valid.
+// IsNetworkAddressV4 validates an IPv4 addresss string.
 func IsNetworkAddressV4(value string) error {
 	ip := net.ParseIP(value)
 	if ip == nil || ip.To4() == nil {
@@ -212,7 +212,7 @@ func IsNetworkAddressV4(value string) error {
 	return nil
 }
 
-// IsNetworkAddressCIDRV4 validates an IPv4 addresss string in CIDR format. If string is empty, returns valid.
+// IsNetworkAddressCIDRV4 validates an IPv4 addresss string in CIDR format.
 func IsNetworkAddressCIDRV4(value string) error {
 	ip, subnet, err := net.ParseCIDR(value)
 	if err != nil {
@@ -256,7 +256,7 @@ func IsNetworkV4List(value string) error {
 	return nil
 }
 
-// IsNetworkV6 validates an IPv6 CIDR string. If string is empty, returns valid.
+// IsNetworkV6 validates an IPv6 CIDR string.
 func IsNetworkV6(value string) error {
 	ip, subnet, err := net.ParseCIDR(value)
 	if err != nil {
@@ -274,7 +274,7 @@ func IsNetworkV6(value string) error {
 	return nil
 }
 
-// IsNetworkAddressV6 validates an IPv6 addresss string. If string is empty, returns valid.
+// IsNetworkAddressV6 validates an IPv6 addresss string.
 func IsNetworkAddressV6(value string) error {
 	ip := net.ParseIP(value)
 	if ip == nil || ip.To4() != nil {
@@ -284,7 +284,7 @@ func IsNetworkAddressV6(value string) error {
 	return nil
 }
 
-// IsNetworkAddressCIDRV6 validates an IPv6 addresss string in CIDR format. If string is empty, returns valid.
+// IsNetworkAddressCIDRV6 validates an IPv6 addresss string in CIDR format.
 func IsNetworkAddressCIDRV6(value string) error {
 	ip, subnet, err := net.ParseCIDR(value)
 	if err != nil {

From 6bc696e845f1e50f717363cb9b9191320452952a Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Fri, 9 Oct 2020 09:39:08 +0100
Subject: [PATCH 2/8] shared/validate/validate: Adds IsNetwork and
 IsNetworkList functions

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

diff --git a/shared/validate/validate.go b/shared/validate/validate.go
index 92939c4e39..3978ad5a80 100644
--- a/shared/validate/validate.go
+++ b/shared/validate/validate.go
@@ -184,6 +184,32 @@ func IsNetworkAddressList(value string) error {
 	return nil
 }
 
+// IsNetwork validates an IP network CIDR string.
+func IsNetwork(value string) error {
+	ip, subnet, err := net.ParseCIDR(value)
+	if err != nil {
+		return err
+	}
+
+	if ip.String() != subnet.IP.String() {
+		return fmt.Errorf("Not an IP network address %q", value)
+	}
+
+	return nil
+}
+
+// IsNetworkList validates a comma delimited list of IP network CIDR strings.
+func IsNetworkList(value string) error {
+	for _, network := range strings.Split(value, ",") {
+		err := IsNetwork(strings.TrimSpace(network))
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
 // IsNetworkV4 validates an IPv4 CIDR string.
 func IsNetworkV4(value string) error {
 	ip, subnet, err := net.ParseCIDR(value)

From 654413bedc13dd4f2d48d886fe5855f9ca395aaf Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Fri, 9 Oct 2020 09:41:09 +0100
Subject: [PATCH 3/8] shared/validate/validate: Re-orders IP validation
 functions

So that IP family functions are together and in logical order (single item validator before list validators).

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 shared/validate/validate.go | 120 ++++++++++++++++++------------------
 1 file changed, 60 insertions(+), 60 deletions(-)

diff --git a/shared/validate/validate.go b/shared/validate/validate.go
index 3978ad5a80..2a84a2bf3e 100644
--- a/shared/validate/validate.go
+++ b/shared/validate/validate.go
@@ -228,6 +228,19 @@ func IsNetworkV4(value string) error {
 	return nil
 }
 
+// IsNetworkV4List validates a comma delimited list of IPv4 CIDR strings.
+func IsNetworkV4List(value string) error {
+	for _, network := range strings.Split(value, ",") {
+		network = strings.TrimSpace(network)
+		err := IsNetworkV4(network)
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
 // IsNetworkAddressV4 validates an IPv4 addresss string.
 func IsNetworkAddressV4(value string) error {
 	ip := net.ParseIP(value)
@@ -238,6 +251,19 @@ func IsNetworkAddressV4(value string) error {
 	return nil
 }
 
+// IsNetworkAddressV4List validates a comma delimited list of IPv4 addresses.
+func IsNetworkAddressV4List(value string) error {
+	for _, v := range strings.Split(value, ",") {
+		v = strings.TrimSpace(v)
+		err := IsNetworkAddressV4(v)
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
 // IsNetworkAddressCIDRV4 validates an IPv4 addresss string in CIDR format.
 func IsNetworkAddressCIDRV4(value string) error {
 	ip, subnet, err := net.ParseCIDR(value)
@@ -256,11 +282,15 @@ func IsNetworkAddressCIDRV4(value string) error {
 	return nil
 }
 
-// IsNetworkAddressV4List validates a comma delimited list of IPv4 addresses.
-func IsNetworkAddressV4List(value string) error {
-	for _, v := range strings.Split(value, ",") {
-		v = strings.TrimSpace(v)
-		err := IsNetworkAddressV4(v)
+// IsNetworkRangeV4 validates an IPv4 range in the format "start-end".
+func IsNetworkRangeV4(value string) error {
+	ips := strings.SplitN(value, "-", 2)
+	if len(ips) != 2 {
+		return fmt.Errorf("IP range must contain start and end IP addresses")
+	}
+
+	for _, ip := range ips {
+		err := IsNetworkAddressV4(ip)
 		if err != nil {
 			return err
 		}
@@ -269,11 +299,10 @@ func IsNetworkAddressV4List(value string) error {
 	return nil
 }
 
-// IsNetworkV4List validates a comma delimited list of IPv4 CIDR strings.
-func IsNetworkV4List(value string) error {
-	for _, network := range strings.Split(value, ",") {
-		network = strings.TrimSpace(network)
-		err := IsNetworkV4(network)
+// IsNetworkRangeV4List validates a comma delimited list of IPv4 ranges.
+func IsNetworkRangeV4List(value string) error {
+	for _, ipRange := range strings.Split(value, ",") {
+		err := IsNetworkRangeV4(strings.TrimSpace(ipRange))
 		if err != nil {
 			return err
 		}
@@ -300,31 +329,26 @@ func IsNetworkV6(value string) error {
 	return nil
 }
 
-// IsNetworkAddressV6 validates an IPv6 addresss string.
-func IsNetworkAddressV6(value string) error {
-	ip := net.ParseIP(value)
-	if ip == nil || ip.To4() != nil {
-		return fmt.Errorf("Not an IPv6 address %q", value)
+// IsNetworkV6List validates a comma delimited list of IPv6 CIDR strings.
+func IsNetworkV6List(value string) error {
+	for _, network := range strings.Split(value, ",") {
+		network = strings.TrimSpace(network)
+		err := IsNetworkV6(network)
+		if err != nil {
+			return err
+		}
 	}
 
 	return nil
 }
 
-// IsNetworkAddressCIDRV6 validates an IPv6 addresss string in CIDR format.
-func IsNetworkAddressCIDRV6(value string) error {
-	ip, subnet, err := net.ParseCIDR(value)
-	if err != nil {
-		return err
-	}
-
-	if ip.To4() != nil {
+// IsNetworkAddressV6 validates an IPv6 addresss string.
+func IsNetworkAddressV6(value string) error {
+	ip := net.ParseIP(value)
+	if ip == nil || ip.To4() != nil {
 		return fmt.Errorf("Not an IPv6 address %q", value)
 	}
 
-	if ip.String() == subnet.IP.String() {
-		return fmt.Errorf("Not a usable IPv6 address %q", value)
-	}
-
 	return nil
 }
 
@@ -340,43 +364,19 @@ func IsNetworkAddressV6List(value string) error {
 	return nil
 }
 
-// IsNetworkV6List validates a comma delimited list of IPv6 CIDR strings.
-func IsNetworkV6List(value string) error {
-	for _, network := range strings.Split(value, ",") {
-		network = strings.TrimSpace(network)
-		err := IsNetworkV6(network)
-		if err != nil {
-			return err
-		}
-	}
-
-	return nil
-}
-
-// IsNetworkRangeV4 validates an IPv4 range in the format "start-end".
-func IsNetworkRangeV4(value string) error {
-	ips := strings.SplitN(value, "-", 2)
-	if len(ips) != 2 {
-		return fmt.Errorf("IP range must contain start and end IP addresses")
+// IsNetworkAddressCIDRV6 validates an IPv6 addresss string in CIDR format.
+func IsNetworkAddressCIDRV6(value string) error {
+	ip, subnet, err := net.ParseCIDR(value)
+	if err != nil {
+		return err
 	}
 
-	for _, ip := range ips {
-		err := IsNetworkAddressV4(ip)
-		if err != nil {
-			return err
-		}
+	if ip.To4() != nil {
+		return fmt.Errorf("Not an IPv6 address %q", value)
 	}
 
-	return nil
-}
-
-// IsNetworkRangeV4List validates a comma delimited list of IPv4 ranges.
-func IsNetworkRangeV4List(value string) error {
-	for _, ipRange := range strings.Split(value, ",") {
-		err := IsNetworkRangeV4(strings.TrimSpace(ipRange))
-		if err != nil {
-			return err
-		}
+	if ip.String() == subnet.IP.String() {
+		return fmt.Errorf("Not a usable IPv6 address %q", value)
 	}
 
 	return nil

From 7a900762a1978b06297a1870514337de45f77cef Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Fri, 9 Oct 2020 10:14:08 +0100
Subject: [PATCH 4/8] lxd/device/nic/ovn: Comment

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

diff --git a/lxd/device/nic_ovn.go b/lxd/device/nic_ovn.go
index 71c77419d8..e499c7cf90 100644
--- a/lxd/device/nic_ovn.go
+++ b/lxd/device/nic_ovn.go
@@ -26,7 +26,7 @@ import (
 type nicOVN struct {
 	deviceCommon
 
-	network network.Network
+	network network.Network // Populated in validateConfig().
 }
 
 // getIntegrationBridgeName returns the OVS integration bridge to use.

From c34ee20298981bedd5d392dad010907e5001a218 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Fri, 9 Oct 2020 10:29:29 +0100
Subject: [PATCH 5/8] doc/api-extensions: Removes mention of "parent" from
 projects_networks_restricted_uplinks feature

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 doc/api-extensions.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/doc/api-extensions.md b/doc/api-extensions.md
index bc3ee9e2d2..a2688afac2 100644
--- a/doc/api-extensions.md
+++ b/doc/api-extensions.md
@@ -1157,7 +1157,7 @@ Adds the `features.networks` config key to projects and the ability for a projec
 
 ## projects\_networks\_restricted\_uplinks
 Adds the `restricted.networks.uplinks` project config key to indicate (as a comma delimited list) which networks
-the networks created inside the project can use as their uplink parent network.
+the networks created inside the project can use as their uplink network.
 
 ## custom\_volume\_backup
 Add custom volume backup support.

From 1b2dc8eba3c670b5794af94fad0226668bc95cf3 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Fri, 9 Oct 2020 10:29:55 +0100
Subject: [PATCH 6/8] doc/networks: Switch to "uplink" terminology for external
 OVN network access

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

diff --git a/doc/networks.md b/doc/networks.md
index 6df04b13c4..cc63de9c11 100644
--- a/doc/networks.md
+++ b/doc/networks.md
@@ -299,7 +299,7 @@ dns.search                      | string    | -                     | -
 ipv4.address                    | string    | standard mode         | random unused subnet      | IPv4 address for the bridge (CIDR notation). Use "none" to turn off IPv4 or "auto" to generate a new one
 ipv6.address                    | string    | standard mode         | random unused subnet      | IPv6 address for the bridge (CIDR notation). Use "none" to turn off IPv6 or "auto" to generate a new one
 ipv6.dhcp.stateful              | boolean   | ipv6 dhcp             | false                     | Whether to allocate addresses using DHCP
-network                         | string    | -                     | -                         | Parent network to use for outbound external network access
+network                         | string    | -                     | -                         | Uplink network to use for external network access
 
 ## network: physical
 

From 6085a3da6a9ee8fb27b4e56fdd788165954b5b57 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Fri, 9 Oct 2020 10:30:21 +0100
Subject: [PATCH 7/8] lxd/network/driver/ovn: Replace parent terminology with
 uplink

Now that "uplink" concept is formalised in `restricted.networks.uplinks` we should use consistent terminilogy in code.

Also "parent" concept is different and used for other meanings, so re-using when talking about uplink is confusing.

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

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index 7387f10270..ca3373c9f4 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -29,11 +29,11 @@ import (
 )
 
 const ovnChassisPriorityMax = 32767
-const ovnVolatileParentIPv4 = "volatile.network.ipv4.address"
-const ovnVolatileParentIPv6 = "volatile.network.ipv6.address"
+const ovnVolatileUplinkIPv4 = "volatile.network.ipv4.address"
+const ovnVolatileUplinkIPv6 = "volatile.network.ipv6.address"
 
-// ovnParentVars OVN object variables derived from parent network.
-type ovnParentVars struct {
+// ovnUplinkVars OVN object variables derived from uplink network.
+type ovnUplinkVars struct {
 	// Router.
 	routerExtPortIPv4Net string
 	routerExtPortIPv6Net string
@@ -48,10 +48,10 @@ type ovnParentVars struct {
 	dnsIPv4 []net.IP
 }
 
-// ovnParentPortBridgeVars parent bridge port variables used for start/stop.
-type ovnParentPortBridgeVars struct {
+// ovnUplinkPortBridgeVars uplink bridge port variables used for start/stop.
+type ovnUplinkPortBridgeVars struct {
 	ovsBridge string
-	parentEnd string
+	uplinkEnd string
 	ovsEnd    string
 }
 
@@ -103,8 +103,8 @@ func (n *ovn) Validate(config map[string]string) error {
 		"dns.search":         validate.IsAny,
 
 		// Volatile keys populated automatically as needed.
-		ovnVolatileParentIPv4: validate.Optional(validate.IsNetworkAddressV4),
-		ovnVolatileParentIPv6: validate.Optional(validate.IsNetworkAddressV6),
+		ovnVolatileUplinkIPv4: validate.Optional(validate.IsNetworkAddressV4),
+		ovnVolatileUplinkIPv6: validate.Optional(validate.IsNetworkAddressV6),
 	}
 
 	err := n.validate(config, rules)
@@ -351,102 +351,102 @@ func (n *ovn) getIntSwitchInstancePortPrefix() string {
 	return fmt.Sprintf("%s-instance", n.getNetworkPrefix())
 }
 
-// setupParentPort initialises the parent uplink connection. Returns the derived ovnParentVars settings used
+// setupUplinkPort initialises the uplink connection. Returns the derived ovnUplinkVars settings used
 // during the initial creation of the logical network.
-func (n *ovn) setupParentPort(routerMAC net.HardwareAddr) (*ovnParentVars, error) {
-	// Parent network must be in default project.
-	parentNet, err := LoadByName(n.state, project.Default, n.config["network"])
+func (n *ovn) setupUplinkPort(routerMAC net.HardwareAddr) (*ovnUplinkVars, error) {
+	// Uplink network must be in default project.
+	uplinkNet, err := LoadByName(n.state, project.Default, n.config["network"])
 	if err != nil {
-		return nil, errors.Wrapf(err, "Failed loading parent network %q", n.config["network"])
+		return nil, errors.Wrapf(err, "Failed loading uplink network %q", n.config["network"])
 	}
 
-	switch parentNet.Type() {
+	switch uplinkNet.Type() {
 	case "bridge":
-		return n.setupParentPortBridge(parentNet, routerMAC)
+		return n.setupUplinkPortBridge(uplinkNet, routerMAC)
 	case "physical":
-		return n.setupParentPortPhysical(parentNet, routerMAC)
+		return n.setupUplinkPortPhysical(uplinkNet, routerMAC)
 	}
 
-	return nil, fmt.Errorf("Failed setting up parent port, network type %q unsupported as OVN parent", parentNet.Type())
+	return nil, fmt.Errorf("Failed setting up uplink port, network type %q unsupported as OVN uplink", uplinkNet.Type())
 }
 
-// setupParentPortBridge allocates external IPs on the parent bridge.
-// Returns the derived ovnParentVars settings.
-func (n *ovn) setupParentPortBridge(parentNet Network, routerMAC net.HardwareAddr) (*ovnParentVars, error) {
-	bridgeNet, ok := parentNet.(*bridge)
+// setupUplinkPortBridge allocates external IPs on the uplink bridge.
+// Returns the derived ovnUplinkVars settings.
+func (n *ovn) setupUplinkPortBridge(uplinkNet Network, routerMAC net.HardwareAddr) (*ovnUplinkVars, error) {
+	bridgeNet, ok := uplinkNet.(*bridge)
 	if !ok {
 		return nil, fmt.Errorf("Network is not bridge type")
 	}
 
 	err := bridgeNet.checkClusterWideMACSafe(bridgeNet.config)
 	if err != nil {
-		return nil, errors.Wrapf(err, "Network %q is not suitable for use as OVN parent", bridgeNet.name)
+		return nil, errors.Wrapf(err, "Network %q is not suitable for use as OVN uplink", bridgeNet.name)
 	}
 
-	v, err := n.allocateParentPortIPs(parentNet, routerMAC)
+	v, err := n.allocateUplinkPortIPs(uplinkNet, routerMAC)
 	if err != nil {
-		return nil, errors.Wrapf(err, "Failed allocating parent port IPs on network %q", parentNet.Name())
+		return nil, errors.Wrapf(err, "Failed allocating uplink port IPs on network %q", uplinkNet.Name())
 	}
 
 	return v, nil
 }
 
-// setupParentPortPhysical allocates external IPs on the parent network.
-// Returns the derived ovnParentVars settings.
-func (n *ovn) setupParentPortPhysical(parentNet Network, routerMAC net.HardwareAddr) (*ovnParentVars, error) {
-	v, err := n.allocateParentPortIPs(parentNet, routerMAC)
+// setupUplinkPortPhysical allocates external IPs on the uplink network.
+// Returns the derived ovnUplinkVars settings.
+func (n *ovn) setupUplinkPortPhysical(uplinkNet Network, routerMAC net.HardwareAddr) (*ovnUplinkVars, error) {
+	v, err := n.allocateUplinkPortIPs(uplinkNet, routerMAC)
 	if err != nil {
-		return nil, errors.Wrapf(err, "Failed allocating parent port IPs on network %q", parentNet.Name())
+		return nil, errors.Wrapf(err, "Failed allocating uplink port IPs on network %q", uplinkNet.Name())
 	}
 
 	return v, nil
 }
 
-// allocateParentPortIPs attempts to find a free IP in the parent network's OVN ranges and then stores it in
-// ovnVolatileParentIPv4 and ovnVolatileParentIPv6 config keys on this network. Returns ovnParentVars settings.
-func (n *ovn) allocateParentPortIPs(parentNet Network, routerMAC net.HardwareAddr) (*ovnParentVars, error) {
-	v := &ovnParentVars{}
+// allocateUplinkPortIPs attempts to find a free IP in the uplink network's OVN ranges and then stores it in
+// ovnVolatileUplinkIPv4 and ovnVolatileUplinkIPv6 config keys on this network. Returns ovnUplinkVars settings.
+func (n *ovn) allocateUplinkPortIPs(uplinkNet Network, routerMAC net.HardwareAddr) (*ovnUplinkVars, error) {
+	v := &ovnUplinkVars{}
 
-	parentNetConf := parentNet.Config()
+	uplinkNetConf := uplinkNet.Config()
 
-	// Parent derived settings.
-	v.extSwitchProviderName = parentNet.Name()
+	// Uplink derived settings.
+	v.extSwitchProviderName = uplinkNet.Name()
 
-	// Detect parent gateway setting.
-	parentIPv4CIDR := parentNetConf["ipv4.address"]
-	if parentIPv4CIDR == "" {
-		parentIPv4CIDR = parentNetConf["ipv4.gateway"]
+	// Detect uplink gateway setting.
+	uplinkIPv4CIDR := uplinkNetConf["ipv4.address"]
+	if uplinkIPv4CIDR == "" {
+		uplinkIPv4CIDR = uplinkNetConf["ipv4.gateway"]
 	}
 
-	parentIPv6CIDR := parentNetConf["ipv6.address"]
-	if parentIPv6CIDR == "" {
-		parentIPv6CIDR = parentNetConf["ipv6.gateway"]
+	uplinkIPv6CIDR := uplinkNetConf["ipv6.address"]
+	if uplinkIPv6CIDR == "" {
+		uplinkIPv6CIDR = uplinkNetConf["ipv6.gateway"]
 	}
 
-	// Optional parent values.
-	parentIPv4, parentIPv4Net, err := net.ParseCIDR(parentIPv4CIDR)
+	// Optional uplink values.
+	uplinkIPv4, uplinkIPv4Net, err := net.ParseCIDR(uplinkIPv4CIDR)
 	if err == nil {
-		v.dnsIPv4 = []net.IP{parentIPv4}
-		v.routerExtGwIPv4 = parentIPv4
+		v.dnsIPv4 = []net.IP{uplinkIPv4}
+		v.routerExtGwIPv4 = uplinkIPv4
 	}
 
-	parentIPv6, parentIPv6Net, err := net.ParseCIDR(parentIPv6CIDR)
+	uplinkIPv6, uplinkIPv6Net, err := net.ParseCIDR(uplinkIPv6CIDR)
 	if err == nil {
-		v.dnsIPv6 = []net.IP{parentIPv6}
-		v.routerExtGwIPv6 = parentIPv6
+		v.dnsIPv6 = []net.IP{uplinkIPv6}
+		v.routerExtGwIPv6 = uplinkIPv6
 	}
 
 	// Detect optional DNS server list.
-	if parentNetConf["dns.nameservers"] != "" {
+	if uplinkNetConf["dns.nameservers"] != "" {
 		// Reset nameservers.
 		v.dnsIPv4 = nil
 		v.dnsIPv6 = nil
 
-		nsList := strings.Split(parentNetConf["dns.nameservers"], ",")
+		nsList := strings.Split(uplinkNetConf["dns.nameservers"], ",")
 		for _, ns := range nsList {
 			nsIP := net.ParseIP(strings.TrimSpace(ns))
 			if nsIP == nil {
-				return nil, fmt.Errorf("Invalid parent nameserver")
+				return nil, fmt.Errorf("Invalid uplink nameserver")
 			}
 
 			if nsIP.To4() == nil {
@@ -457,63 +457,63 @@ func (n *ovn) allocateParentPortIPs(parentNet Network, routerMAC net.HardwareAdd
 		}
 	}
 
-	// Parse existing allocated IPs for this network on the parent network (if not set yet, will be nil).
-	routerExtPortIPv4 := net.ParseIP(n.config[ovnVolatileParentIPv4])
-	routerExtPortIPv6 := net.ParseIP(n.config[ovnVolatileParentIPv6])
+	// Parse existing allocated IPs for this network on the uplink network (if not set yet, will be nil).
+	routerExtPortIPv4 := net.ParseIP(n.config[ovnVolatileUplinkIPv4])
+	routerExtPortIPv6 := net.ParseIP(n.config[ovnVolatileUplinkIPv6])
 
 	// Decide whether we need to allocate new IP(s) and go to the expense of retrieving all allocated IPs.
-	if (parentIPv4Net != nil && routerExtPortIPv4 == nil) || (parentIPv6Net != nil && routerExtPortIPv6 == nil) {
+	if (uplinkIPv4Net != nil && routerExtPortIPv4 == nil) || (uplinkIPv6Net != nil && routerExtPortIPv6 == nil) {
 		err := n.state.Cluster.Transaction(func(tx *db.ClusterTx) error {
-			allAllocatedIPv4, allAllocatedIPv6, err := n.parentAllAllocatedIPs(tx, parentNet.Name())
+			allAllocatedIPv4, allAllocatedIPv6, err := n.uplinkAllAllocatedIPs(tx, uplinkNet.Name())
 			if err != nil {
-				return errors.Wrapf(err, "Failed to get all allocated IPs for parent")
+				return errors.Wrapf(err, "Failed to get all allocated IPs for uplink")
 			}
 
-			if parentIPv4Net != nil && routerExtPortIPv4 == nil {
-				if parentNetConf["ipv4.ovn.ranges"] == "" {
-					return fmt.Errorf(`Missing required "ipv4.ovn.ranges" config key on parent network`)
+			if uplinkIPv4Net != nil && routerExtPortIPv4 == nil {
+				if uplinkNetConf["ipv4.ovn.ranges"] == "" {
+					return fmt.Errorf(`Missing required "ipv4.ovn.ranges" config key on uplink network`)
 				}
 
-				ipRanges, err := parseIPRanges(parentNetConf["ipv4.ovn.ranges"], parentNet.DHCPv4Subnet())
+				ipRanges, err := parseIPRanges(uplinkNetConf["ipv4.ovn.ranges"], uplinkNet.DHCPv4Subnet())
 				if err != nil {
-					return errors.Wrapf(err, "Failed to parse parent IPv4 OVN ranges")
+					return errors.Wrapf(err, "Failed to parse uplink IPv4 OVN ranges")
 				}
 
-				routerExtPortIPv4, err = n.parentAllocateIP(ipRanges, allAllocatedIPv4)
+				routerExtPortIPv4, err = n.uplinkAllocateIP(ipRanges, allAllocatedIPv4)
 				if err != nil {
-					return errors.Wrapf(err, "Failed to allocate parent IPv4 address")
+					return errors.Wrapf(err, "Failed to allocate uplink IPv4 address")
 				}
 
-				n.config[ovnVolatileParentIPv4] = routerExtPortIPv4.String()
+				n.config[ovnVolatileUplinkIPv4] = routerExtPortIPv4.String()
 			}
 
-			if parentIPv6Net != nil && routerExtPortIPv6 == nil {
-				// If IPv6 OVN ranges are specified by the parent, allocate from them.
-				if parentNetConf["ipv6.ovn.ranges"] != "" {
-					ipRanges, err := parseIPRanges(parentNetConf["ipv6.ovn.ranges"], parentNet.DHCPv6Subnet())
+			if uplinkIPv6Net != nil && routerExtPortIPv6 == nil {
+				// If IPv6 OVN ranges are specified by the uplink, allocate from them.
+				if uplinkNetConf["ipv6.ovn.ranges"] != "" {
+					ipRanges, err := parseIPRanges(uplinkNetConf["ipv6.ovn.ranges"], uplinkNet.DHCPv6Subnet())
 					if err != nil {
-						return errors.Wrapf(err, "Failed to parse parent IPv6 OVN ranges")
+						return errors.Wrapf(err, "Failed to parse uplink IPv6 OVN ranges")
 					}
 
-					routerExtPortIPv6, err = n.parentAllocateIP(ipRanges, allAllocatedIPv6)
+					routerExtPortIPv6, err = n.uplinkAllocateIP(ipRanges, allAllocatedIPv6)
 					if err != nil {
-						return errors.Wrapf(err, "Failed to allocate parent IPv6 address")
+						return errors.Wrapf(err, "Failed to allocate uplink IPv6 address")
 					}
 
 				} else {
 					// Otherwise use EUI64 derived from MAC address.
-					routerExtPortIPv6, err = eui64.ParseMAC(parentIPv6Net.IP, routerMAC)
+					routerExtPortIPv6, err = eui64.ParseMAC(uplinkIPv6Net.IP, routerMAC)
 					if err != nil {
 						return err
 					}
 				}
 
-				n.config[ovnVolatileParentIPv6] = routerExtPortIPv6.String()
+				n.config[ovnVolatileUplinkIPv6] = routerExtPortIPv6.String()
 			}
 
 			err = tx.UpdateNetwork(n.id, n.description, n.config)
 			if err != nil {
-				return errors.Wrapf(err, "Failed saving allocated parent network IPs")
+				return errors.Wrapf(err, "Failed saving allocated uplink network IPs")
 			}
 
 			return nil
@@ -524,17 +524,17 @@ func (n *ovn) allocateParentPortIPs(parentNet Network, routerMAC net.HardwareAdd
 	}
 
 	// Configure variables needed to configure OVN router.
-	if parentIPv4Net != nil && routerExtPortIPv4 != nil {
+	if uplinkIPv4Net != nil && routerExtPortIPv4 != nil {
 		routerExtPortIPv4Net := &net.IPNet{
-			Mask: parentIPv4Net.Mask,
+			Mask: uplinkIPv4Net.Mask,
 			IP:   routerExtPortIPv4,
 		}
 		v.routerExtPortIPv4Net = routerExtPortIPv4Net.String()
 	}
 
-	if parentIPv6Net != nil {
+	if uplinkIPv6Net != nil {
 		routerExtPortIPv6Net := &net.IPNet{
-			Mask: parentIPv6Net.Mask,
+			Mask: uplinkIPv6Net.Mask,
 			IP:   routerExtPortIPv6,
 		}
 		v.routerExtPortIPv6Net = routerExtPortIPv6Net.String()
@@ -543,8 +543,8 @@ func (n *ovn) allocateParentPortIPs(parentNet Network, routerMAC net.HardwareAdd
 	return v, nil
 }
 
-// parentAllAllocatedIPs gets a list of all IPv4 and IPv6 addresses allocated to OVN networks connected to parent.
-func (n *ovn) parentAllAllocatedIPs(tx *db.ClusterTx, parentNetName string) ([]net.IP, []net.IP, error) {
+// uplinkAllAllocatedIPs gets a list of all IPv4 and IPv6 addresses allocated to OVN networks connected to uplink.
+func (n *ovn) uplinkAllAllocatedIPs(tx *db.ClusterTx, uplinkNetName string) ([]net.IP, []net.IP, error) {
 	// Get all managed networks across all projects.
 	projectNetworks, err := tx.GetNonPendingNetworks()
 	if err != nil {
@@ -556,11 +556,11 @@ func (n *ovn) parentAllAllocatedIPs(tx *db.ClusterTx, parentNetName string) ([]n
 
 	for _, networks := range projectNetworks {
 		for _, netInfo := range networks {
-			if netInfo.Type != "ovn" || netInfo.Config["network"] != parentNetName {
+			if netInfo.Type != "ovn" || netInfo.Config["network"] != uplinkNetName {
 				continue
 			}
 
-			for _, k := range []string{ovnVolatileParentIPv4, ovnVolatileParentIPv6} {
+			for _, k := range []string{ovnVolatileUplinkIPv4, ovnVolatileUplinkIPv6} {
 				if netInfo.Config[k] != "" {
 					ip := net.ParseIP(netInfo.Config[k])
 					if ip != nil {
@@ -578,8 +578,8 @@ func (n *ovn) parentAllAllocatedIPs(tx *db.ClusterTx, parentNetName string) ([]n
 	return v4IPs, v6IPs, nil
 }
 
-// parentAllocateIP allocates a free IP from one of the IP ranges.
-func (n *ovn) parentAllocateIP(ipRanges []*shared.IPRange, allAllocated []net.IP) (net.IP, error) {
+// uplinkAllocateIP allocates a free IP from one of the IP ranges.
+func (n *ovn) uplinkAllocateIP(ipRanges []*shared.IPRange, allAllocated []net.IP) (net.IP, error) {
 	for _, ipRange := range ipRanges {
 		inc := big.NewInt(1)
 
@@ -629,73 +629,73 @@ func (n *ovn) parentAllocateIP(ipRanges []*shared.IPRange, allAllocated []net.IP
 	return nil, fmt.Errorf("No free IPs available")
 }
 
-// startParentPort performs any network start up logic needed to connect the parent uplink connection to OVN.
-func (n *ovn) startParentPort() error {
-	// Parent network must be in default project.
-	parentNet, err := LoadByName(n.state, project.Default, n.config["network"])
+// startUplinkPort performs any network start up logic needed to connect the uplink connection to OVN.
+func (n *ovn) startUplinkPort() error {
+	// Uplink network must be in default project.
+	uplinkNet, err := LoadByName(n.state, project.Default, n.config["network"])
 	if err != nil {
-		return errors.Wrapf(err, "Failed loading parent network")
+		return errors.Wrapf(err, "Failed loading uplink network")
 	}
 
-	// Lock parent network so that if multiple OVN networks are trying to connect to the same parent we don't
+	// Lock uplink network so that if multiple OVN networks are trying to connect to the same uplink we don't
 	// race each other setting up the connection.
-	unlock := locking.Lock(n.parentOperationLockName(parentNet))
+	unlock := locking.Lock(n.uplinkOperationLockName(uplinkNet))
 	defer unlock()
 
-	switch parentNet.Type() {
+	switch uplinkNet.Type() {
 	case "bridge":
-		return n.startParentPortBridge(parentNet)
+		return n.startUplinkPortBridge(uplinkNet)
 	case "physical":
-		return n.startParentPortPhysical(parentNet)
+		return n.startUplinkPortPhysical(uplinkNet)
 	}
 
-	return fmt.Errorf("Failed starting parent port, network type %q unsupported as OVN parent", parentNet.Type())
+	return fmt.Errorf("Failed starting uplink port, network type %q unsupported as OVN uplink", uplinkNet.Type())
 }
 
-// parentOperationLockName returns the lock name to use for operations on the parent network.
-func (n *ovn) parentOperationLockName(parentNet Network) string {
-	return fmt.Sprintf("network.ovn.%s", parentNet.Name())
+// uplinkOperationLockName returns the lock name to use for operations on the uplink network.
+func (n *ovn) uplinkOperationLockName(uplinkNet Network) string {
+	return fmt.Sprintf("network.ovn.%s", uplinkNet.Name())
 }
 
-// parentPortBridgeVars returns the parent port bridge variables needed for port start/stop.
-func (n *ovn) parentPortBridgeVars(parentNet Network) *ovnParentPortBridgeVars {
-	ovsBridge := fmt.Sprintf("lxdovn%d", parentNet.ID())
+// uplinkPortBridgeVars returns the uplink port bridge variables needed for port start/stop.
+func (n *ovn) uplinkPortBridgeVars(uplinkNet Network) *ovnUplinkPortBridgeVars {
+	ovsBridge := fmt.Sprintf("lxdovn%d", uplinkNet.ID())
 
-	return &ovnParentPortBridgeVars{
+	return &ovnUplinkPortBridgeVars{
 		ovsBridge: ovsBridge,
-		parentEnd: fmt.Sprintf("%sa", ovsBridge),
+		uplinkEnd: fmt.Sprintf("%sa", ovsBridge),
 		ovsEnd:    fmt.Sprintf("%sb", ovsBridge),
 	}
 }
 
-// startParentPortBridge creates veth pair (if doesn't exist), creates OVS bridge (if doesn't exist) and
-// connects veth pair to parent bridge and OVS bridge.
-func (n *ovn) startParentPortBridge(parentNet Network) error {
-	vars := n.parentPortBridgeVars(parentNet)
+// startUplinkPortBridge creates veth pair (if doesn't exist), creates OVS bridge (if doesn't exist) and
+// connects veth pair to uplink bridge and OVS bridge.
+func (n *ovn) startUplinkPortBridge(uplinkNet Network) error {
+	vars := n.uplinkPortBridgeVars(uplinkNet)
 
 	// Do this after gaining lock so that on failure we revert before release locking.
 	revert := revert.New()
 	defer revert.Fail()
 
 	// Create veth pair if needed.
-	if !InterfaceExists(vars.parentEnd) && !InterfaceExists(vars.ovsEnd) {
-		_, err := shared.RunCommand("ip", "link", "add", "dev", vars.parentEnd, "type", "veth", "peer", "name", vars.ovsEnd)
+	if !InterfaceExists(vars.uplinkEnd) && !InterfaceExists(vars.ovsEnd) {
+		_, err := shared.RunCommand("ip", "link", "add", "dev", vars.uplinkEnd, "type", "veth", "peer", "name", vars.ovsEnd)
 		if err != nil {
-			return errors.Wrapf(err, "Failed to create the uplink veth interfaces %q and %q", vars.parentEnd, vars.ovsEnd)
+			return errors.Wrapf(err, "Failed to create the uplink veth interfaces %q and %q", vars.uplinkEnd, vars.ovsEnd)
 		}
 
-		revert.Add(func() { shared.RunCommand("ip", "link", "delete", vars.parentEnd) })
+		revert.Add(func() { shared.RunCommand("ip", "link", "delete", vars.uplinkEnd) })
 	}
 
 	// Ensure that the veth interfaces inherit the uplink bridge's MTU (which the OVS bridge also inherits).
-	parentNetConfig := parentNet.Config()
-	if parentNetConfig["bridge.mtu"] != "" {
-		err := InterfaceSetMTU(vars.parentEnd, parentNetConfig["bridge.mtu"])
+	uplinkNetConfig := uplinkNet.Config()
+	if uplinkNetConfig["bridge.mtu"] != "" {
+		err := InterfaceSetMTU(vars.uplinkEnd, uplinkNetConfig["bridge.mtu"])
 		if err != nil {
 			return err
 		}
 
-		err = InterfaceSetMTU(vars.ovsEnd, parentNetConfig["bridge.mtu"])
+		err = InterfaceSetMTU(vars.ovsEnd, uplinkNetConfig["bridge.mtu"])
 		if err != nil {
 			return err
 		}
@@ -703,55 +703,55 @@ func (n *ovn) startParentPortBridge(parentNet Network) error {
 
 	// Ensure correct sysctls are set on uplink veth interfaces to avoid getting IPv6 link-local addresses.
 	err := util.SysctlSet(
-		fmt.Sprintf("net/ipv6/conf/%s/disable_ipv6", vars.parentEnd), "1",
+		fmt.Sprintf("net/ipv6/conf/%s/disable_ipv6", vars.uplinkEnd), "1",
 		fmt.Sprintf("net/ipv6/conf/%s/disable_ipv6", vars.ovsEnd), "1",
-		fmt.Sprintf("net/ipv6/conf/%s/forwarding", vars.parentEnd), "0",
+		fmt.Sprintf("net/ipv6/conf/%s/forwarding", vars.uplinkEnd), "0",
 		fmt.Sprintf("net/ipv6/conf/%s/forwarding", vars.ovsEnd), "0",
 	)
 	if err != nil {
-		return errors.Wrapf(err, "Failed to configure uplink veth interfaces %q and %q", vars.parentEnd, vars.ovsEnd)
+		return errors.Wrapf(err, "Failed to configure uplink veth interfaces %q and %q", vars.uplinkEnd, vars.ovsEnd)
 	}
 
-	// Connect parent end of veth pair to parent bridge and bring up.
-	_, err = shared.RunCommand("ip", "link", "set", "master", parentNet.Name(), "dev", vars.parentEnd, "up")
+	// Connect uplink end of veth pair to uplink bridge and bring up.
+	_, err = shared.RunCommand("ip", "link", "set", "master", uplinkNet.Name(), "dev", vars.uplinkEnd, "up")
 	if err != nil {
-		return errors.Wrapf(err, "Failed to connect uplink veth interface %q to parent bridge %q", vars.parentEnd, parentNet.Name())
+		return errors.Wrapf(err, "Failed to connect uplink veth interface %q to uplink bridge %q", vars.uplinkEnd, uplinkNet.Name())
 	}
 
 	// Ensure uplink OVS end veth interface is up.
 	_, err = shared.RunCommand("ip", "link", "set", "dev", vars.ovsEnd, "up")
 	if err != nil {
-		return errors.Wrapf(err, "Failed to bring up parent veth interface %q", vars.ovsEnd)
+		return errors.Wrapf(err, "Failed to bring up uplink veth interface %q", vars.ovsEnd)
 	}
 
-	// Create parent OVS bridge if needed.
+	// Create uplink OVS bridge if needed.
 	ovs := openvswitch.NewOVS()
 	err = ovs.BridgeAdd(vars.ovsBridge, true)
 	if err != nil {
-		return errors.Wrapf(err, "Failed to create parent uplink OVS bridge %q", vars.ovsBridge)
+		return errors.Wrapf(err, "Failed to create uplink OVS bridge %q", vars.ovsBridge)
 	}
 
 	// Connect OVS end veth interface to OVS bridge.
 	err = ovs.BridgePortAdd(vars.ovsBridge, vars.ovsEnd, true)
 	if err != nil {
-		return errors.Wrapf(err, "Failed to connect uplink veth interface %q to parent OVS bridge %q", vars.ovsEnd, vars.ovsBridge)
+		return errors.Wrapf(err, "Failed to connect uplink veth interface %q to uplink OVS bridge %q", vars.ovsEnd, vars.ovsBridge)
 	}
 
 	// Associate OVS bridge to logical OVN provider.
-	err = ovs.OVNBridgeMappingAdd(vars.ovsBridge, parentNet.Name())
+	err = ovs.OVNBridgeMappingAdd(vars.ovsBridge, uplinkNet.Name())
 	if err != nil {
-		return errors.Wrapf(err, "Failed to associate parent OVS bridge %q to OVN provider %q", vars.ovsBridge, parentNet.Name())
+		return errors.Wrapf(err, "Failed to associate uplink OVS bridge %q to OVN provider %q", vars.ovsBridge, uplinkNet.Name())
 	}
 
-	routerExtPortIPv6 := net.ParseIP(n.config[ovnVolatileParentIPv6])
+	routerExtPortIPv6 := net.ParseIP(n.config[ovnVolatileUplinkIPv6])
 	if routerExtPortIPv6 != nil {
-		// Now that the OVN router is connected to the uplink parent bridge, attempt to ping the OVN
-		// router's external IPv6 from the LXD host running the parent bridge in an attempt to trigger the
-		// OVN router to learn the parent uplink gateway's MAC address. This is to work around a bug in
+		// Now that the OVN router is connected to the uplink bridge, attempt to ping the OVN
+		// router's external IPv6 from the LXD host running the uplink bridge in an attempt to trigger the
+		// OVN router to learn the uplink gateway's MAC address. This is to work around a bug in
 		// older versions of OVN that meant that the OVN router would not attempt to learn the external
 		// uplink IPv6 gateway MAC address when using SNAT, meaning that external IPv6 connectivity
 		// 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
+		// By pinging the OVN router's external IP this will trigger an NDP request from the uplink bridge
 		// which will cause the OVN router to learn its MAC address.
 		go func() {
 			// Try several attempts as it can take a few seconds for the network to come up.
@@ -774,19 +774,19 @@ func (n *ovn) startParentPortBridge(parentNet Network) error {
 	return nil
 }
 
-// startParentPortPhysical creates OVS bridge (if doesn't exist) and connects parent interface to the OVS bridge.
-func (n *ovn) startParentPortPhysical(parentNet Network) error {
-	vars := n.parentPortBridgeVars(parentNet)
+// startUplinkPortPhysical creates OVS bridge (if doesn't exist) and connects uplink interface to the OVS bridge.
+func (n *ovn) startUplinkPortPhysical(uplinkNet Network) error {
+	vars := n.uplinkPortBridgeVars(uplinkNet)
 
 	// Do this after gaining lock so that on failure we revert before release locking.
 	revert := revert.New()
 	defer revert.Fail()
 
-	parentConfig := parentNet.Config()
-	uplinkHostName := GetHostDevice(parentConfig["parent"], parentConfig["vlan"])
+	uplinkConfig := uplinkNet.Config()
+	uplinkHostName := GetHostDevice(uplinkConfig["parent"], uplinkConfig["vlan"])
 
 	if !InterfaceExists(uplinkHostName) {
-		return fmt.Errorf("Uplink network %q is not started", parentNet.Name())
+		return fmt.Errorf("Uplink network %q is not started", uplinkNet.Name())
 	}
 
 	// Ensure correct sysctls are set on uplink interface to avoid getting IPv6 link-local addresses.
@@ -798,29 +798,29 @@ func (n *ovn) startParentPortPhysical(parentNet Network) error {
 		return errors.Wrapf(err, "Failed to configure uplink interface %q", uplinkHostName)
 	}
 
-	// Create parent OVS bridge if needed.
+	// Create uplink OVS bridge if needed.
 	ovs := openvswitch.NewOVS()
 	err = ovs.BridgeAdd(vars.ovsBridge, true)
 	if err != nil {
-		return errors.Wrapf(err, "Failed to create parent uplink OVS bridge %q", vars.ovsBridge)
+		return errors.Wrapf(err, "Failed to create uplink OVS bridge %q", vars.ovsBridge)
 	}
 
 	// Connect OVS end veth interface to OVS bridge.
 	err = ovs.BridgePortAdd(vars.ovsBridge, uplinkHostName, true)
 	if err != nil {
-		return errors.Wrapf(err, "Failed to connect uplink interface %q to parent OVS bridge %q", uplinkHostName, vars.ovsBridge)
+		return errors.Wrapf(err, "Failed to connect uplink interface %q to uplink OVS bridge %q", uplinkHostName, vars.ovsBridge)
 	}
 
 	// Associate OVS bridge to logical OVN provider.
-	err = ovs.OVNBridgeMappingAdd(vars.ovsBridge, parentNet.Name())
+	err = ovs.OVNBridgeMappingAdd(vars.ovsBridge, uplinkNet.Name())
 	if err != nil {
-		return errors.Wrapf(err, "Failed to associate parent OVS bridge %q to OVN provider %q", vars.ovsBridge, parentNet.Name())
+		return errors.Wrapf(err, "Failed to associate uplink OVS bridge %q to OVN provider %q", vars.ovsBridge, uplinkNet.Name())
 	}
 
 	// Bring uplink interface up.
 	_, err = shared.RunCommand("ip", "link", "set", uplinkHostName, "up")
 	if err != nil {
-		return errors.Wrapf(err, "Failed to bring up parent interface %q", uplinkHostName)
+		return errors.Wrapf(err, "Failed to bring up uplink interface %q", uplinkHostName)
 	}
 
 	revert.Success()
@@ -847,7 +847,7 @@ func (n *ovn) checkUplinkUse() (bool, error) {
 				continue // Ignore our own DB record or non OVN networks.
 			}
 
-			// Check if another network is using our parent.
+			// Check if another network is using our uplink.
 			if network.Config["network"] == n.config["network"] {
 				return true, nil
 			}
@@ -857,37 +857,37 @@ func (n *ovn) checkUplinkUse() (bool, error) {
 	return false, nil
 }
 
-// deleteParentPort deletes the parent uplink connection.
-func (n *ovn) deleteParentPort() error {
-	// Parent network must be in default project.
+// deleteUplinkPort deletes the uplink connection.
+func (n *ovn) deleteUplinkPort() error {
+	// Uplink network must be in default project.
 	if n.config["network"] != "" {
-		parentNet, err := LoadByName(n.state, project.Default, n.config["network"])
+		uplinkNet, err := LoadByName(n.state, project.Default, n.config["network"])
 		if err != nil {
-			return errors.Wrapf(err, "Failed loading parent network")
+			return errors.Wrapf(err, "Failed loading uplink network")
 		}
 
-		// Lock parent network so we don't race each other networks using the OVS uplink bridge.
-		unlock := locking.Lock(n.parentOperationLockName(parentNet))
+		// Lock uplink network so we don't race each other networks using the OVS uplink bridge.
+		unlock := locking.Lock(n.uplinkOperationLockName(uplinkNet))
 		defer unlock()
 
-		switch parentNet.Type() {
+		switch uplinkNet.Type() {
 		case "bridge":
-			return n.deleteParentPortBridge(parentNet)
+			return n.deleteUplinkPortBridge(uplinkNet)
 		case "physical":
-			return n.deleteParentPortPhysical(parentNet)
+			return n.deleteUplinkPortPhysical(uplinkNet)
 		}
 
-		return fmt.Errorf("Failed deleting parent port, network type %q unsupported as OVN parent", parentNet.Type())
+		return fmt.Errorf("Failed deleting uplink port, network type %q unsupported as OVN uplink", uplinkNet.Type())
 	}
 
 	return nil
 }
 
-// deleteParentPortBridge deletes parent uplink OVS bridge, OVN bridge mappings and veth interfaces if not in use.
-func (n *ovn) deleteParentPortBridge(parentNet Network) error {
+// deleteUplinkPortBridge deletes uplink OVS bridge, OVN bridge mappings and veth interfaces if not in use.
+func (n *ovn) deleteUplinkPortBridge(uplinkNet Network) error {
 	// Check OVS uplink bridge exists, if it does, check whether the uplink network is in use.
 	removeVeths := false
-	vars := n.parentPortBridgeVars(parentNet)
+	vars := n.uplinkPortBridgeVars(uplinkNet)
 	if InterfaceExists(vars.ovsBridge) {
 		uplinkUsed, err := n.checkUplinkUse()
 		if err != nil {
@@ -899,7 +899,7 @@ func (n *ovn) deleteParentPortBridge(parentNet Network) error {
 			removeVeths = true
 
 			ovs := openvswitch.NewOVS()
-			err = ovs.OVNBridgeMappingDelete(vars.ovsBridge, parentNet.Name())
+			err = ovs.OVNBridgeMappingDelete(vars.ovsBridge, uplinkNet.Name())
 			if err != nil {
 				return err
 			}
@@ -915,10 +915,10 @@ func (n *ovn) deleteParentPortBridge(parentNet Network) error {
 
 	// Remove the veth interfaces if they exist.
 	if removeVeths {
-		if InterfaceExists(vars.parentEnd) {
-			_, err := shared.RunCommand("ip", "link", "delete", "dev", vars.parentEnd)
+		if InterfaceExists(vars.uplinkEnd) {
+			_, err := shared.RunCommand("ip", "link", "delete", "dev", vars.uplinkEnd)
 			if err != nil {
-				return errors.Wrapf(err, "Failed to delete the uplink veth interface %q", vars.parentEnd)
+				return errors.Wrapf(err, "Failed to delete the uplink veth interface %q", vars.uplinkEnd)
 			}
 		}
 
@@ -933,11 +933,11 @@ func (n *ovn) deleteParentPortBridge(parentNet Network) error {
 	return nil
 }
 
-// deleteParentPortPhysical deletes parent uplink OVS bridge and OVN bridge mappings if not in use.
-func (n *ovn) deleteParentPortPhysical(parentNet Network) error {
+// deleteUplinkPortPhysical deletes uplink OVS bridge and OVN bridge mappings if not in use.
+func (n *ovn) deleteUplinkPortPhysical(uplinkNet Network) error {
 	// Check OVS uplink bridge exists, if it does, check whether the uplink network is in use.
 	releaseIF := false
-	vars := n.parentPortBridgeVars(parentNet)
+	vars := n.uplinkPortBridgeVars(uplinkNet)
 	if InterfaceExists(vars.ovsBridge) {
 		uplinkUsed, err := n.checkUplinkUse()
 		if err != nil {
@@ -949,7 +949,7 @@ func (n *ovn) deleteParentPortPhysical(parentNet Network) error {
 			releaseIF = true
 
 			ovs := openvswitch.NewOVS()
-			err = ovs.OVNBridgeMappingDelete(vars.ovsBridge, parentNet.Name())
+			err = ovs.OVNBridgeMappingDelete(vars.ovsBridge, uplinkNet.Name())
 			if err != nil {
 				return err
 			}
@@ -965,12 +965,12 @@ func (n *ovn) deleteParentPortPhysical(parentNet Network) error {
 
 	// Bring down uplink interface if exists.
 	if releaseIF {
-		parentConfig := parentNet.Config()
-		parentDev := GetHostDevice(parentConfig["parent"], parentConfig["vlan"])
-		if InterfaceExists(parentDev) {
-			_, err := shared.RunCommand("ip", "link", "set", parentDev, "down")
+		uplinkConfig := uplinkNet.Config()
+		uplinkDev := GetHostDevice(uplinkConfig["parent"], uplinkConfig["vlan"])
+		if InterfaceExists(uplinkDev) {
+			_, err := shared.RunCommand("ip", "link", "set", uplinkDev, "down")
 			if err != nil {
-				return errors.Wrapf(err, "Failed to bring down uplink interface %q", parentDev)
+				return errors.Wrapf(err, "Failed to bring down uplink interface %q", uplinkDev)
 			}
 		}
 	}
@@ -1171,24 +1171,24 @@ func (n *ovn) setup(update bool) error {
 		return err
 	}
 
-	// Setup parent port (do this first to check parent is suitable).
-	parent, err := n.setupParentPort(routerMAC)
+	// Setup uplink port (do this first to check uplink is suitable).
+	uplinkNet, err := n.setupUplinkPort(routerMAC)
 	if err != nil {
 		return err
 	}
 
 	// Parse router IP config.
-	if parent.routerExtPortIPv4Net != "" {
-		routerExtPortIPv4, routerExtPortIPv4Net, err = net.ParseCIDR(parent.routerExtPortIPv4Net)
+	if uplinkNet.routerExtPortIPv4Net != "" {
+		routerExtPortIPv4, routerExtPortIPv4Net, err = net.ParseCIDR(uplinkNet.routerExtPortIPv4Net)
 		if err != nil {
-			return errors.Wrapf(err, "Failed parsing router's external parent port IPv4 Net")
+			return errors.Wrapf(err, "Failed parsing router's external uplink port IPv4 Net")
 		}
 	}
 
-	if parent.routerExtPortIPv6Net != "" {
-		routerExtPortIPv6, routerExtPortIPv6Net, err = net.ParseCIDR(parent.routerExtPortIPv6Net)
+	if uplinkNet.routerExtPortIPv6Net != "" {
+		routerExtPortIPv6, routerExtPortIPv6Net, err = net.ParseCIDR(uplinkNet.routerExtPortIPv6Net)
 		if err != nil {
-			return errors.Wrapf(err, "Failed parsing router's external parent port IPv6 Net")
+			return errors.Wrapf(err, "Failed parsing router's external uplink port IPv6 Net")
 		}
 	}
 
@@ -1228,15 +1228,15 @@ func (n *ovn) setup(update bool) error {
 	// Configure logical router.
 
 	// Add default routes.
-	if parent.routerExtGwIPv4 != nil {
-		err = client.LogicalRouterRouteAdd(n.getRouterName(), &net.IPNet{IP: net.IPv4zero, Mask: net.CIDRMask(0, 32)}, parent.routerExtGwIPv4)
+	if uplinkNet.routerExtGwIPv4 != nil {
+		err = client.LogicalRouterRouteAdd(n.getRouterName(), &net.IPNet{IP: net.IPv4zero, Mask: net.CIDRMask(0, 32)}, uplinkNet.routerExtGwIPv4)
 		if err != nil {
 			return errors.Wrapf(err, "Failed adding IPv4 default route")
 		}
 	}
 
-	if parent.routerExtGwIPv6 != nil {
-		err = client.LogicalRouterRouteAdd(n.getRouterName(), &net.IPNet{IP: net.IPv6zero, Mask: net.CIDRMask(0, 128)}, parent.routerExtGwIPv6)
+	if uplinkNet.routerExtGwIPv6 != nil {
+		err = client.LogicalRouterRouteAdd(n.getRouterName(), &net.IPNet{IP: net.IPv6zero, Mask: net.CIDRMask(0, 128)}, uplinkNet.routerExtGwIPv6)
 		if err != nil {
 			return errors.Wrapf(err, "Failed adding IPv6 default route")
 		}
@@ -1317,7 +1317,7 @@ func (n *ovn) setup(update bool) error {
 		}
 		revert.Add(func() { client.LogicalSwitchPortDelete(n.getExtSwitchProviderPortName()) })
 
-		err = client.LogicalSwitchPortLinkProviderNetwork(n.getExtSwitchProviderPortName(), parent.extSwitchProviderName)
+		err = client.LogicalSwitchPortLinkProviderNetwork(n.getExtSwitchProviderPortName(), uplinkNet.extSwitchProviderName)
 		if err != nil {
 			return errors.Wrapf(err, "Failed linking external switch provider port to external provider network")
 		}
@@ -1367,7 +1367,7 @@ func (n *ovn) setup(update bool) error {
 		ServerID:           routerIntPortIPv4,
 		ServerMAC:          routerMAC,
 		Router:             routerIntPortIPv4,
-		RecursiveDNSServer: parent.dnsIPv4,
+		RecursiveDNSServer: uplinkNet.dnsIPv4,
 		DomainName:         n.getDomainName(),
 		LeaseTime:          time.Duration(time.Hour * 1),
 		MTU:                bridgeMTU,
@@ -1379,7 +1379,7 @@ func (n *ovn) setup(update bool) error {
 	// Create DHCPv6 options for internal switch.
 	err = client.LogicalSwitchDHCPv6OptionsSet(n.getIntSwitchName(), dhcpv6UUID, routerIntPortIPv6Net, &openvswitch.OVNDHCPv6Opts{
 		ServerID:           routerMAC,
-		RecursiveDNSServer: parent.dnsIPv6,
+		RecursiveDNSServer: uplinkNet.dnsIPv6,
 		DNSSearchList:      n.getDNSSearchList(),
 	})
 	if err != nil {
@@ -1417,8 +1417,8 @@ func (n *ovn) setup(update bool) error {
 		}
 
 		var recursiveDNSServer net.IP
-		if len(parent.dnsIPv6) > 0 {
-			recursiveDNSServer = parent.dnsIPv6[0] // OVN only supports 1 RA DNS server.
+		if len(uplinkNet.dnsIPv6) > 0 {
+			recursiveDNSServer = uplinkNet.dnsIPv6[0] // OVN only supports 1 RA DNS server.
 		}
 
 		err = client.LogicalRouterPortSetIPv6Advertisements(n.getRouterIntPortName(), &openvswitch.OVNIPv6RAOpts{
@@ -1577,7 +1577,7 @@ func (n *ovn) Rename(newName string) error {
 	return nil
 }
 
-// Start starts adds the local OVS chassis ID to the OVN chass group and starts the local OVS parent uplink port.
+// Start starts adds the local OVS chassis ID to the OVN chass group and starts the local OVS uplink port.
 func (n *ovn) Start() error {
 	n.logger.Debug("Start")
 
@@ -1591,7 +1591,7 @@ func (n *ovn) Start() error {
 		return err
 	}
 
-	err = n.startParentPort()
+	err = n.startUplinkPort()
 	if err != nil {
 		return err
 	}
@@ -1599,7 +1599,7 @@ func (n *ovn) Start() error {
 	return nil
 }
 
-// Stop deletes the local OVS parent uplink port (if unused) and deletes the local OVS chassis ID from the
+// Stop deletes the local OVS uplink port (if unused) and deletes the local OVS chassis ID from the
 // OVN chass group
 func (n *ovn) Stop() error {
 	n.logger.Debug("Stop")
@@ -1610,8 +1610,8 @@ func (n *ovn) Stop() error {
 		return err
 	}
 
-	// Delete local parent uplink port if not used by other OVN networks.
-	err = n.deleteParentPort()
+	// Delete local uplink port if not used by other OVN networks.
+	err = n.deleteUplinkPort()
 	if err != nil {
 		return err
 	}
@@ -1663,8 +1663,8 @@ func (n *ovn) Update(newNetwork api.NetworkPut, targetNode string, clientType cl
 		}
 
 		// Remove volatile keys associated with old network in new config.
-		delete(newNetwork.Config, ovnVolatileParentIPv4)
-		delete(newNetwork.Config, ovnVolatileParentIPv6)
+		delete(newNetwork.Config, ovnVolatileUplinkIPv4)
+		delete(newNetwork.Config, ovnVolatileUplinkIPv6)
 	}
 
 	// Apply changes to all nodes and databse.

From 406d4fc37c9b00a0065e237fc74e0122acea92a6 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 13 Oct 2020 09:36:01 +0100
Subject: [PATCH 8/8] lxd/network/driver/common: Ban : char from network names
 in ValidateName()

This prevents network names from conflicting with vlan alias interface names and when used as a prefix for `restricted.networks.subnets` in projects.

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

diff --git a/lxd/network/driver_common.go b/lxd/network/driver_common.go
index 3c422c5a1a..23cff9a58c 100644
--- a/lxd/network/driver_common.go
+++ b/lxd/network/driver_common.go
@@ -101,7 +101,16 @@ func (n *common) validate(config map[string]string, driverRules map[string]func(
 
 // ValidateName validates network name.
 func (n *common) ValidateName(name string) error {
-	return validate.IsURLSegmentSafe(name)
+	err := validate.IsURLSegmentSafe(name)
+	if err != nil {
+		return err
+	}
+
+	if strings.Contains(name, ":") {
+		return fmt.Errorf("Cannot contain %q", ":")
+	}
+
+	return nil
 }
 
 // ID returns the network ID.


More information about the lxc-devel mailing list