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

This e-mail was sent by the LXC bot, direct replies will not reach the author
unless they happen to be subscribed to this list.

=== Description (from pull-request) ===
These are only used on OVN NICs now.
From 71caae866af04c142dbb48cc19a4d7a126e3657a Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Mon, 19 Oct 2020 15:56:33 +0100
Subject: [PATCH 01/15] lxd/network/driver/ovn: Only call Validate in
 FillConfig if state is set

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

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index ea1616d269..cfc5e7fff2 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -1168,7 +1168,7 @@ func (n *ovn) FillConfig(config map[string]string) error {
                changedConfig = true
        }
 
-       if changedConfig {
+       if changedConfig && n.state != nil {
                return n.Validate(config)
        }
 

From f6add9084341a88c0847398eb4db5610e7e5f1bd Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Mon, 19 Oct 2020 15:56:15 +0100
Subject: [PATCH 02/15] lxd/db/projects: Adds GetProject function

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

diff --git a/lxd/db/projects.go b/lxd/db/projects.go
index cf03765ea5..ff613611d1 100644
--- a/lxd/db/projects.go
+++ b/lxd/db/projects.go
@@ -189,3 +189,22 @@ func (c *ClusterTx) InitProjectWithoutImages(project 
string) error {
        _, err = c.tx.Exec(stmt, defaultProfileID)
        return err
 }
+
+// GetProject returns the project with the given key.
+func (c *Cluster) GetProject(projectName string) (*api.Project, error) {
+       var err error
+       var p *api.Project
+       err = c.Transaction(func(tx *ClusterTx) error {
+               p, err = tx.GetProject(projectName)
+               if err != nil {
+                       return err
+               }
+
+               return nil
+       })
+       if err != nil {
+               return nil, err
+       }
+
+       return p, nil
+}

From fa2da560919d515ecd24a2aebc40b4a213c34fea Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Mon, 19 Oct 2020 13:56:53 +0100
Subject: [PATCH 03/15] lxd/network/driver/ovn: Converts instance port
 functions to exported

So they can be accessed by OVN NIC directly.

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

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index cfc5e7fff2..25780813e1 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -1887,8 +1887,8 @@ func (n *ovn) getInstanceDevicePortName(instanceID int, 
deviceName string) openv
        return openvswitch.OVNSwitchPort(fmt.Sprintf("%s-%d-%s", 
n.getIntSwitchInstancePortPrefix(), instanceID, deviceName))
 }
 
-// instanceDevicePortAdd adds an instance device port to the internal logical 
switch and returns the port name.
-func (n *ovn) instanceDevicePortAdd(instanceID int, instanceName string, 
deviceName string, mac net.HardwareAddr, ips []net.IP, internalRoutes 
[]*net.IPNet, externalRoutes []*net.IPNet) (openvswitch.OVNSwitchPort, error) {
+// InstanceDevicePortAdd adds an instance device port to the internal logical 
switch and returns the port name.
+func (n *ovn) InstanceDevicePortAdd(instanceID int, instanceName string, 
deviceName string, mac net.HardwareAddr, ips []net.IP, internalRoutes 
[]*net.IPNet, externalRoutes []*net.IPNet) (openvswitch.OVNSwitchPort, error) {
        var dhcpV4ID, dhcpv6ID string
 
        revert := revert.New()
@@ -2065,8 +2065,8 @@ func (n *ovn) instanceDevicePortAdd(instanceID int, 
instanceName string, deviceN
        return instancePortName, nil
 }
 
-// instanceDevicePortIPs returns the dynamically allocated IPs for a device 
port.
-func (n *ovn) instanceDevicePortDynamicIPs(instanceID int, deviceName string) 
([]net.IP, error) {
+// InstanceDevicePortDynamicIPs returns the dynamically allocated IPs for a 
device port.
+func (n *ovn) InstanceDevicePortDynamicIPs(instanceID int, deviceName string) 
([]net.IP, error) {
        instancePortName := n.getInstanceDevicePortName(instanceID, deviceName)
 
        client, err := n.getClient()
@@ -2077,8 +2077,8 @@ func (n *ovn) instanceDevicePortDynamicIPs(instanceID 
int, deviceName string) ([
        return client.LogicalSwitchPortDynamicIPs(instancePortName)
 }
 
-// instanceDevicePortDelete deletes an instance device port from the internal 
logical switch.
-func (n *ovn) instanceDevicePortDelete(instanceID int, deviceName string, 
internalRoutes []*net.IPNet, externalRoutes []*net.IPNet) error {
+// InstanceDevicePortDelete deletes an instance device port from the internal 
logical switch.
+func (n *ovn) InstanceDevicePortDelete(instanceID int, deviceName string, 
internalRoutes []*net.IPNet, externalRoutes []*net.IPNet) error {
        instancePortName := n.getInstanceDevicePortName(instanceID, deviceName)
 
        client, err := n.getClient()

From d5351219834cbb9bb12faba3cf653f0751765898 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Mon, 19 Oct 2020 14:02:16 +0100
Subject: [PATCH 04/15] lxd/network/driver/ovn: Removes ipv4.routes.external
 and ipv6.routes.external

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

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index 25780813e1..91463e5477 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -133,13 +133,11 @@ func (n *ovn) Validate(config map[string]string) error {
 
                        return 
validate.Optional(validate.IsNetworkAddressCIDRV6)(value)
                },
-               "ipv6.dhcp.stateful":   validate.Optional(validate.IsBool),
-               "ipv4.routes.external": 
validate.Optional(validate.IsNetworkV4List),
-               "ipv6.routes.external": 
validate.Optional(validate.IsNetworkV6List),
-               "ipv4.nat":             validate.Optional(validate.IsBool),
-               "ipv6.nat":             validate.Optional(validate.IsBool),
-               "dns.domain":           validate.IsAny,
-               "dns.search":           validate.IsAny,
+               "ipv6.dhcp.stateful": validate.Optional(validate.IsBool),
+               "ipv4.nat":           validate.Optional(validate.IsBool),
+               "ipv6.nat":           validate.Optional(validate.IsBool),
+               "dns.domain":         validate.IsAny,
+               "dns.search":         validate.IsAny,
 
                // Volatile keys populated automatically as needed.
                ovnVolatileUplinkIPv4: 
validate.Optional(validate.IsNetworkAddressV4),
@@ -232,28 +230,6 @@ func (n *ovn) Validate(config map[string]string) error {
                }
        }
 
-       // Check IP external routes are within the uplink network's routes and 
project's subnet restrictions.
-       if config["ipv4.routes.external"] != "" || 
config["ipv6.routes.external"] != "" {
-               // Parse and validate our external routes.
-               for _, k := range []string{"ipv4.routes.external", 
"ipv6.routes.external"} {
-                       if config[k] == "" {
-                               continue
-                       }
-
-                       routeSubnetList, err := 
SubnetParseAppend([]*net.IPNet{}, strings.Split(config[k], ",")...)
-                       if err != nil {
-                               return err
-                       }
-
-                       for _, routeSubnet := range routeSubnetList {
-                               err = n.validateExternalSubnet(uplinkRoutes, 
projectRestrictedSubnets, routeSubnet)
-                               if err != nil {
-                                       return err
-                               }
-                       }
-               }
-       }
-
        return nil
 }
 

From e1668b6f4f92365854ad48eb4ee243186c87fa15 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Mon, 19 Oct 2020 17:00:06 +0100
Subject: [PATCH 05/15] lxc/network/driver/ovn: Adds projectRestrictedSubnets
 and uplinkRoutes functions

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

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index 91463e5477..1e537c7413 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -78,6 +78,61 @@ func (n *ovn) Info() Info {
        }
 }
 
+// uplinkRoutes parses ipv4.routes and ipv6.routes settings for a named uplink 
network into a slice of *net.IPNet.
+func (n *ovn) uplinkRoutes(uplinkNetworkName string) ([]*net.IPNet, error) {
+       _, uplink, err := n.state.Cluster.GetNetworkInAnyState(project.Default, 
uplinkNetworkName)
+       if err != nil {
+               return nil, err
+       }
+
+       var uplinkRoutes []*net.IPNet
+       for _, k := range []string{"ipv4.routes", "ipv6.routes"} {
+               if uplink.Config[k] == "" {
+                       continue
+               }
+
+               uplinkRoutes, err = SubnetParseAppend(uplinkRoutes, 
strings.Split(uplink.Config[k], ",")...)
+               if err != nil {
+                       return nil, err
+               }
+       }
+
+       return uplinkRoutes, nil
+}
+
+// projectRestrictedSubnets parses the restrict.networks.subnets project 
setting and returns slice of *net.IPNet.
+// Returns nil slice if no project restrictions, or empty slice if no allowed 
subnets.
+func (n *ovn) projectRestrictedSubnets(p *api.Project, uplinkNetworkName 
string) ([]*net.IPNet, error) {
+       // Parse project's restricted subnets.
+       var projectRestrictedSubnets []*net.IPNet // Nil value indicates not 
restricted.
+       if shared.IsTrue(p.Config["restricted"]) && 
p.Config["restricted.networks.subnets"] != "" {
+               projectRestrictedSubnets = []*net.IPNet{} // Empty slice 
indicates no allowed subnets.
+
+               for _, subnetRaw := range 
strings.Split(p.Config["restricted.networks.subnets"], ",") {
+                       subnetParts := 
strings.SplitN(strings.TrimSpace(subnetRaw), ":", 2)
+                       if len(subnetParts) != 2 {
+                               return nil, fmt.Errorf(`Project subnet %q 
invalid, must be in the format of "<uplink network>:<subnet>"`, subnetRaw)
+                       }
+
+                       subnetUplinkName := subnetParts[0]
+                       subnetStr := subnetParts[1]
+
+                       if subnetUplinkName != uplinkNetworkName {
+                               continue // Only include subnets for our uplink.
+                       }
+
+                       _, restrictedSubnet, err := net.ParseCIDR(subnetStr)
+                       if err != nil {
+                               return nil, err
+                       }
+
+                       projectRestrictedSubnets = 
append(projectRestrictedSubnets, restrictedSubnet)
+               }
+       }
+
+       return projectRestrictedSubnets, nil
+}
+
 // validateExternalSubnet checks the supplied ipNet is allowed within the 
uplink routes and project
 // restricted subnets. If projectRestrictedSubnets is nil, then it is not 
checked as this indicates project has
 // no restrictions. Whereas if uplinkRoutes is nil/empty then this will always 
return an error.

From bbfce066807e2491abe1bd281cccc0ac6c8f7b26 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Mon, 19 Oct 2020 17:00:30 +0100
Subject: [PATCH 06/15] lxd/network/driver/ovn: Simplifies Validate by using
 separate data loader functions

Also removes a duplicate project load query from DB.

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

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index 1e537c7413..8d7b55111f 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -205,69 +205,27 @@ func (n *ovn) Validate(config map[string]string) error {
        }
 
        // Load the project to get uplink network restrictions.
-       var projectRow *api.Project
-       err = n.state.Cluster.Transaction(func(tx *db.ClusterTx) error {
-               projectRow, err = tx.GetProject(n.project)
-               if err != nil {
-                       return err
-               }
-
-               return nil
-       })
+       p, err := n.state.Cluster.GetProject(n.project)
        if err != nil {
-               return errors.Wrapf(err, "Failed to load IP route restrictions 
for project %q", n.project)
+               return errors.Wrapf(err, "Failed to load network restrictions 
from project %q", n.project)
        }
 
        // Check uplink network is valid and allowed in project.
-       uplinkNetworkName, err := n.validateUplinkNetwork(config["network"])
+       uplinkNetworkName, err := n.validateUplinkNetwork(p, config["network"])
        if err != nil {
                return err
        }
 
-       // Load the uplink network to get uplink routes.
-       _, uplink, err := n.state.Cluster.GetNetworkInAnyState(project.Default, 
uplinkNetworkName)
+       // Get uplink routes.
+       uplinkRoutes, err := n.uplinkRoutes(uplinkNetworkName)
        if err != nil {
                return err
        }
 
-       // Parse uplink route subnets.
-       var uplinkRoutes []*net.IPNet
-       for _, k := range []string{"ipv4.routes", "ipv6.routes"} {
-               if uplink.Config[k] == "" {
-                       continue
-               }
-
-               uplinkRoutes, err = SubnetParseAppend(uplinkRoutes, 
strings.Split(uplink.Config[k], ",")...)
-               if err != nil {
-                       return err
-               }
-       }
-
-       // Parse project's restricted subnets.
-       var projectRestrictedSubnets []*net.IPNet // Nil value indicates not 
restricted.
-       if shared.IsTrue(projectRow.Config["restricted"]) && 
projectRow.Config["restricted.networks.subnets"] != "" {
-               projectRestrictedSubnets = []*net.IPNet{} // Empty slice 
indicates no allowed subnets.
-
-               for _, subnetRaw := range 
strings.Split(projectRow.Config["restricted.networks.subnets"], ",") {
-                       subnetParts := 
strings.SplitN(strings.TrimSpace(subnetRaw), ":", 2)
-                       if len(subnetParts) != 2 {
-                               return fmt.Errorf(`Project subnet %q invalid, 
must be in the format of "<uplink network>:<subnet>"`, subnetRaw)
-                       }
-
-                       uplinkName := subnetParts[0]
-                       subnetStr := subnetParts[1]
-
-                       if uplinkName != uplink.Name {
-                               continue // Only include subnets for our uplink.
-                       }
-
-                       _, restrictedSubnet, err := net.ParseCIDR(subnetStr)
-                       if err != nil {
-                               return err
-                       }
-
-                       projectRestrictedSubnets = 
append(projectRestrictedSubnets, restrictedSubnet)
-               }
+       // Get project restricted routes.
+       projectRestrictedSubnets, err := n.projectRestrictedSubnets(p, 
uplinkNetworkName)
+       if err != nil {
+               return err
        }
 
        // If NAT disabled, check subnets are within the uplink network's 
routes and project's subnet restrictions.

From d84254815bcb4e822fad4ef67f6a2c84a4c5a76e Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Mon, 19 Oct 2020 17:01:14 +0100
Subject: [PATCH 07/15] lxd/network/driver/ovn: Passes project into
 allowedUplinkNetworks

Rather than load again from DB.

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

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index 8d7b55111f..f401f08fd0 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -1180,7 +1180,7 @@ func (n *ovn) Create(clientType cluster.ClientType) error 
{
 }
 
 // allowedUplinkNetworks returns a list of allowed networks to use as uplinks 
based on project restrictions.
-func (n *ovn) allowedUplinkNetworks() ([]string, error) {
+func (n *ovn) allowedUplinkNetworks(p *api.Project) ([]string, error) {
        // Uplink networks are always from the default project.
        networks, err := n.state.Cluster.GetNetworks(project.Default)
        if err != nil {
@@ -1200,34 +1200,20 @@ func (n *ovn) allowedUplinkNetworks() ([]string, error) 
{
                }
        }
 
-       // Load the project to get uplink network restrictions.
-       var project *api.Project
-       err = n.state.Cluster.Transaction(func(tx *db.ClusterTx) error {
-               project, err = tx.GetProject(n.project)
-               if err != nil {
-                       return err
-               }
-
-               return nil
-       })
-       if err != nil {
-               return nil, errors.Wrapf(err, "Failed to load restrictions for 
project %q", n.project)
-       }
-
        // If project is not restricted, return full network list.
-       if !shared.IsTrue(project.Config["restricted"]) {
+       if !shared.IsTrue(p.Config["restricted"]) {
                return networks, nil
        }
 
        allowedNetworks := []string{}
 
        // There are no allowed networks if restricted.networks.uplinks is not 
set.
-       if project.Config["restricted.networks.uplinks"] == "" {
+       if p.Config["restricted.networks.uplinks"] == "" {
                return allowedNetworks, nil
        }
 
        // Parse the allowed uplinks and return any that are present in the 
actual defined networks.
-       allowedRestrictedUplinks := 
strings.Split(project.Config["restricted.networks.uplinks"], ",")
+       allowedRestrictedUplinks := 
strings.Split(p.Config["restricted.networks.uplinks"], ",")
 
        for _, allowedRestrictedUplink := range allowedRestrictedUplinks {
                allowedRestrictedUplink = 
strings.TrimSpace(allowedRestrictedUplink)

From bfd7ac77861bdc804e742948aa0c7c12b38946c2 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Mon, 19 Oct 2020 17:01:43 +0100
Subject: [PATCH 08/15] lxd/network/driver/ovn: Passes project into
 validateUplinkNetwork

To avoid additional query.

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

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index f401f08fd0..9ea0806cf2 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -1229,8 +1229,8 @@ func (n *ovn) allowedUplinkNetworks(p *api.Project) 
([]string, error) {
 // validateUplinkNetwork checks if uplink network is allowed, and if empty 
string is supplied then tries to select
 // an uplink network from the allowedUplinkNetworks() list if there is only 
one allowed network.
 // Returns chosen uplink network name to use.
-func (n *ovn) validateUplinkNetwork(uplinkNetworkName string) (string, error) {
-       allowedUplinkNetworks, err := n.allowedUplinkNetworks()
+func (n *ovn) validateUplinkNetwork(p *api.Project, uplinkNetworkName string) 
(string, error) {
+       allowedUplinkNetworks, err := n.allowedUplinkNetworks(p)
        if err != nil {
                return "", err
        }

From 44e6104b2a3acccb81bbc4a6c2c8f5eca43da058 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Mon, 19 Oct 2020 17:02:09 +0100
Subject: [PATCH 09/15] lxd/network/driver/ovn: Load project in setup() to pass
 to n.validateUplinkNetwork()

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

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index 9ea0806cf2..8f568c54c5 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -1276,8 +1276,14 @@ func (n *ovn) setup(update bool) error {
        // Record updated config so we can store back into DB and n.config 
variable.
        updatedConfig := make(map[string]string)
 
+       // Load the project to get uplink network restrictions.
+       p, err := n.state.Cluster.GetProject(n.project)
+       if err != nil {
+               return errors.Wrapf(err, "Failed to load network restrictions 
from project %q", n.project)
+       }
+
        // Check project restrictions and get uplink network to use.
-       uplinkNetwork, err := n.validateUplinkNetwork(n.config["network"])
+       uplinkNetwork, err := n.validateUplinkNetwork(p, n.config["network"])
        if err != nil {
                return err
        }

From 3efb1adc6d469353639768c632549ca846b65d38 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Mon, 19 Oct 2020 17:02:30 +0100
Subject: [PATCH 10/15] lxd/network/driver/ovn: Adds
 InstanceDevicePortValidateExternalRoutes function

To validate a OVN NIC's external routes against an OVN network's uplink routes 
and project's restricted subnet.

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

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index 8f568c54c5..1898ad2f4a 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -1868,6 +1868,36 @@ func (n *ovn) getInstanceDevicePortName(instanceID int, 
deviceName string) openv
        return openvswitch.OVNSwitchPort(fmt.Sprintf("%s-%d-%s", 
n.getIntSwitchInstancePortPrefix(), instanceID, deviceName))
 }
 
+// InstanceDevicePortValidateExternalRoutes validates the external routes for 
an OVN instance port.
+func (n *ovn) InstanceDevicePortValidateExternalRoutes(externalRoutes 
[]*net.IPNet) error {
+       // Load the project to get uplink network restrictions.
+       p, err := n.state.Cluster.GetProject(n.project)
+       if err != nil {
+               return errors.Wrapf(err, "Failed to load network restrictions 
from project %q", n.project)
+       }
+
+       // Get uplink routes.
+       uplinkRoutes, err := n.uplinkRoutes(n.config["network"])
+       if err != nil {
+               return err
+       }
+
+       // Get project restricted routes.
+       projectRestrictedSubnets, err := n.projectRestrictedSubnets(p, 
n.config["network"])
+       if err != nil {
+               return err
+       }
+
+       for _, externalRoute := range externalRoutes {
+               err = n.validateExternalSubnet(uplinkRoutes, 
projectRestrictedSubnets, externalRoute)
+               if err != nil {
+                       return err
+               }
+       }
+
+       return nil
+}
+
 // InstanceDevicePortAdd adds an instance device port to the internal logical 
switch and returns the port name.
 func (n *ovn) InstanceDevicePortAdd(instanceID int, instanceName string, 
deviceName string, mac net.HardwareAddr, ips []net.IP, internalRoutes 
[]*net.IPNet, externalRoutes []*net.IPNet) (openvswitch.OVNSwitchPort, error) {
        var dhcpV4ID, dhcpv6ID string

From 131a644a8507295a9c25882e88a48ba952e125bf Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Mon, 19 Oct 2020 13:55:55 +0100
Subject: [PATCH 11/15] lxd/network/network/utils/ovn: Remvoes unused functions

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/network/network_utils_ovn.go | 42 --------------------------------
 1 file changed, 42 deletions(-)
 delete mode 100644 lxd/network/network_utils_ovn.go

diff --git a/lxd/network/network_utils_ovn.go b/lxd/network/network_utils_ovn.go
deleted file mode 100644
index 41b8e89d27..0000000000
--- a/lxd/network/network_utils_ovn.go
+++ /dev/null
@@ -1,42 +0,0 @@
-package network
-
-import (
-       "fmt"
-       "net"
-
-       "github.com/lxc/lxd/lxd/network/openvswitch"
-)
-
-// OVNInstanceDevicePortAdd adds a logical port to the OVN network's internal 
switch and returns the logical
-// port name for use linking an OVS port on the integration bridge to the 
logical switch port.
-func OVNInstanceDevicePortAdd(network Network, instanceID int, instanceName 
string, deviceName string, mac net.HardwareAddr, ips []net.IP, internalRoutes 
[]*net.IPNet, externalRoutes []*net.IPNet) (openvswitch.OVNSwitchPort, error) {
-       // Check network is of type OVN.
-       n, ok := network.(*ovn)
-       if !ok {
-               return "", fmt.Errorf("Network is not OVN type")
-       }
-
-       return n.instanceDevicePortAdd(instanceID, instanceName, deviceName, 
mac, ips, internalRoutes, externalRoutes)
-}
-
-// OVNInstanceDevicePortDynamicIPs gets a logical port's dynamic IPs stored in 
the OVN network's internal switch.
-func OVNInstanceDevicePortDynamicIPs(network Network, instanceID int, 
deviceName string) ([]net.IP, error) {
-       // Check network is of type OVN.
-       n, ok := network.(*ovn)
-       if !ok {
-               return nil, fmt.Errorf("Network is not OVN type")
-       }
-
-       return n.instanceDevicePortDynamicIPs(instanceID, deviceName)
-}
-
-// OVNInstanceDevicePortDelete deletes a logical port from the OVN network's 
internal switch.
-func OVNInstanceDevicePortDelete(network Network, instanceID int, deviceName 
string, internalRoutes []*net.IPNet, externalRoutes []*net.IPNet) error {
-       // Check network is of type OVN.
-       n, ok := network.(*ovn)
-       if !ok {
-               return fmt.Errorf("Network is not OVN type")
-       }
-
-       return n.instanceDevicePortDelete(instanceID, deviceName, 
internalRoutes, externalRoutes)
-}

From 08415e9d128c15abf020089325ba5c090ceb0e7d Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Mon, 19 Oct 2020 13:58:09 +0100
Subject: [PATCH 12/15] lxd/device/nic/ovn: Adds ovnNet interface and use OVN
 instance port functions directly from network

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

diff --git a/lxd/device/nic_ovn.go b/lxd/device/nic_ovn.go
index 091888ab60..99c49ea622 100644
--- a/lxd/device/nic_ovn.go
+++ b/lxd/device/nic_ovn.go
@@ -24,10 +24,20 @@ import (
        log "github.com/lxc/lxd/shared/log15"
 )
 
+// ovnNet defines an interface for accessing instance specific functions on 
OVN network.
+type ovnNet interface {
+       network.Network
+
+       InstanceDevicePortValidateExternalRoutes(externalRoutes []*net.IPNet) 
error
+       InstanceDevicePortAdd(instanceID int, instanceName string, deviceName 
string, mac net.HardwareAddr, ips []net.IP, internalRoutes []*net.IPNet, 
externalRoutes []*net.IPNet) (openvswitch.OVNSwitchPort, error)
+       InstanceDevicePortDelete(instanceID int, deviceName string, 
internalRoutes []*net.IPNet, externalRoutes []*net.IPNet) error
+       InstanceDevicePortDynamicIPs(instanceID int, deviceName string) 
([]net.IP, error)
+}
+
 type nicOVN struct {
        deviceCommon
 
-       network network.Network // Populated in validateConfig().
+       network ovnNet // Populated in validateConfig().
 }
 
 // getIntegrationBridgeName returns the OVS integration bridge to use.
@@ -91,7 +101,12 @@ func (d *nicOVN) validateConfig(instConf 
instance.ConfigReader) error {
                }
        }
 
-       d.network = n // Stored loaded instance for use by other functions.
+       ovnNet, ok := n.(ovnNet)
+       if !ok {
+               return fmt.Errorf("Network is not OVN type")
+       }
+
+       d.network = ovnNet // Stored loaded instance for use by other functions.
        netConfig := d.network.Config()
 
        if d.config["ipv4.address"] != "" {
@@ -317,14 +332,12 @@ func (d *nicOVN) Start() (*deviceConfig.RunConfig, error) 
{
        }
 
        // Add new OVN logical switch port for instance.
-       logicalPortName, err := network.OVNInstanceDevicePortAdd(d.network, 
d.inst.ID(), d.inst.Name(), d.name, mac, ips, internalRoutes, externalRoutes)
+       logicalPortName, err := d.network.InstanceDevicePortAdd(d.inst.ID(), 
d.inst.Name(), d.name, mac, ips, internalRoutes, externalRoutes)
        if err != nil {
                return nil, errors.Wrapf(err, "Failed adding OVN port")
        }
 
-       revert.Add(func() {
-               network.OVNInstanceDevicePortDelete(d.network, d.inst.ID(), 
d.name, internalRoutes, externalRoutes)
-       })
+       revert.Add(func() { d.network.InstanceDevicePortDelete(d.inst.ID(), 
d.name, internalRoutes, externalRoutes) })
 
        // Attach host side veth interface to bridge.
        integrationBridge, err := d.getIntegrationBridgeName()
@@ -455,7 +468,7 @@ func (d *nicOVN) Stop() (*deviceConfig.RunConfig, error) {
                }
        }
 
-       err = network.OVNInstanceDevicePortDelete(d.network, d.inst.ID(), 
d.name, internalRoutes, externalRoutes)
+       err = d.network.InstanceDevicePortDelete(d.inst.ID(), d.name, 
internalRoutes, externalRoutes)
        if err != nil {
                // Don't fail here as we still want the postStop hook to run to 
clean up the local veth pair.
                d.logger.Error("Failed to remove OVN device port", 
log.Ctx{"err": err})
@@ -531,7 +544,7 @@ func (d *nicOVN) State() (*api.InstanceStateNetwork, error) 
{
 
        // OVN only supports dynamic IP allocation if neither IPv4 or IPv6 are 
statically set.
        if d.config["ipv4.address"] == "" && d.config["ipv6.address"] == "" {
-               dynamicIPs, err := 
network.OVNInstanceDevicePortDynamicIPs(d.network, d.inst.ID(), d.name)
+               dynamicIPs, err := 
d.network.InstanceDevicePortDynamicIPs(d.inst.ID(), d.name)
                if err == nil {
                        for _, dynamicIP := range dynamicIPs {
                                family := "inet"

From b9b1964381a0851d00c552fac3519dd02daf28ad Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Mon, 19 Oct 2020 17:03:40 +0100
Subject: [PATCH 13/15] lxd/device/nic/ovn: Removes validation of external
 routes against network's external routes

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/device/nic_ovn.go | 50 -------------------------------------------
 1 file changed, 50 deletions(-)

diff --git a/lxd/device/nic_ovn.go b/lxd/device/nic_ovn.go
index 99c49ea622..16b1dff6cd 100644
--- a/lxd/device/nic_ovn.go
+++ b/lxd/device/nic_ovn.go
@@ -145,56 +145,6 @@ func (d *nicOVN) validateConfig(instConf 
instance.ConfigReader) error {
                }
        }
 
-       // Check IP external routes are within the network's external routes.
-       if d.config["ipv4.routes.external"] != "" || 
d.config["ipv6.routes.external"] != "" {
-               // Parse network external route subnets.
-               var networkRoutes []*net.IPNet
-               for _, k := range []string{"ipv4.routes.external", 
"ipv6.routes.external"} {
-                       if netConfig[k] == "" {
-                               continue
-                       }
-
-                       networkRoutes, err = 
network.SubnetParseAppend(networkRoutes, strings.Split(netConfig[k], ",")...)
-                       if err != nil {
-                               return err
-                       }
-               }
-
-               // Parse and validate our external routes.
-               for _, k := range []string{"ipv4.routes.external", 
"ipv6.routes.external"} {
-                       if d.config[k] == "" {
-                               continue
-                       }
-
-                       externalRoutes, err := 
network.SubnetParseAppend([]*net.IPNet{}, strings.Split(d.config[k], ",")...)
-                       if err != nil {
-                               return err
-                       }
-
-                       for _, externalRoute := range externalRoutes {
-                               rOnes, rBits := externalRoute.Mask.Size()
-                               if rBits > 32 && rOnes < 122 {
-                                       return fmt.Errorf("External route %q is 
too large. Maximum size for IPv6 external route is /122", 
externalRoute.String())
-                               } else if rOnes < 26 {
-                                       return fmt.Errorf("External route %q is 
too large. Maximum size for IPv4 external route is /26", externalRoute.String())
-                               }
-
-                               // Check that the external route is within the 
network's routes.
-                               foundMatch := false
-                               for _, networkRoute := range networkRoutes {
-                                       if network.SubnetContains(networkRoute, 
externalRoute) {
-                                               foundMatch = true
-                                               break
-                                       }
-                               }
-
-                               if !foundMatch {
-                                       return fmt.Errorf("Network %q doesn't 
contain %q in its external routes", n.Name(), externalRoute.String())
-                               }
-                       }
-               }
-       }
-
        // Apply network level config options to device config before 
validation.
        d.config["mtu"] = fmt.Sprintf("%s", netConfig["bridge.mtu"])
 

From 78d4ec8e820ad4f41f42887565b2f112442f3fff Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Mon, 19 Oct 2020 17:04:11 +0100
Subject: [PATCH 14/15] lxd/device/nic/ovn: Validate NICs external routes using
 d.network.InstanceDevicePortValidateExternalRoutes

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

diff --git a/lxd/device/nic_ovn.go b/lxd/device/nic_ovn.go
index 16b1dff6cd..f743083165 100644
--- a/lxd/device/nic_ovn.go
+++ b/lxd/device/nic_ovn.go
@@ -156,6 +156,35 @@ func (d *nicOVN) validateConfig(instConf 
instance.ConfigReader) error {
                return err
        }
 
+       // Check IP external routes are within the network's external routes.
+       var externalRoutes []*net.IPNet
+       for _, k := range []string{"ipv4.routes.external", 
"ipv6.routes.external"} {
+               if d.config[k] == "" {
+                       continue
+               }
+
+               externalRoutes, err = network.SubnetParseAppend(externalRoutes, 
strings.Split(d.config[k], ",")...)
+               if err != nil {
+                       return err
+               }
+       }
+
+       if len(externalRoutes) > 0 {
+               for _, externalRoute := range externalRoutes {
+                       rOnes, rBits := externalRoute.Mask.Size()
+                       if rBits > 32 && rOnes < 122 {
+                               return fmt.Errorf("External route %q is too 
large. Maximum size for IPv6 external route is /122", externalRoute.String())
+                       } else if rOnes < 26 {
+                               return fmt.Errorf("External route %q is too 
large. Maximum size for IPv4 external route is /26", externalRoute.String())
+                       }
+               }
+
+               err = 
d.network.InstanceDevicePortValidateExternalRoutes(externalRoutes)
+               if err != nil {
+                       return err
+               }
+       }
+
        return nil
 }
 

From 4a2a968c0e4e330d00d2c65d434bf9101f883c43 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Mon, 19 Oct 2020 17:06:19 +0100
Subject: [PATCH 15/15] doc/networks: Removes ipv4.routes.external and
 ipv6.routes.external from ovn network

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 doc/networks.md | 2 --
 1 file changed, 2 deletions(-)

diff --git a/doc/networks.md b/doc/networks.md
index d03da41f9d..dc6e109a2e 100644
--- a/doc/networks.md
+++ b/doc/networks.md
@@ -298,11 +298,9 @@ dns.domain                      | string    | -            
         | lxd
 dns.search                      | string    | -                     | -        
                 | Full comma separated domain search list, defaulting to 
`dns.domain` value
 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
 ipv4.nat                        | boolean   | ipv4 address          | false    
                 | Whether to NAT (will default to true if unset and a random 
ipv4.address is generated)
-ipv4.routes.external            | string    | ipv4 address          | -        
                 | Comma separated list of additional external IPv4 CIDR 
subnets that are allowed for OVN NICs ipv4.routes.external setting
 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
 ipv6.nat                        | boolean   | ipv6 address          | false    
                 | Whether to NAT (will default to true if unset and a random 
ipv6.address is generated)
-ipv6.routes.external            | string    | ipv6 address          | -        
                 | Comma separated list of additional external IPv6 CIDR 
subnets that are allowed for OVN NICs ipv6.routes.external setting
 network                         | string    | -                     | -        
                 | Uplink network to use for external network access
 
 ## network: physical
_______________________________________________
lxc-devel mailing list
lxc-devel@lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to