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

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) ===
Don't tear down all OVN config and rebuild, instead try and apply only changes, so as to reduce impact on instance port config.
From cba9502715754f6ae05cfa8bb4a4dae4e62c8465 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Fri, 18 Dec 2020 11:45:16 +0000
Subject: [PATCH 01/16] lxd/network/openvswitch/ovn: Adds mayExist argument to
 LogicalRouterAdd

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

diff --git a/lxd/network/openvswitch/ovn.go b/lxd/network/openvswitch/ovn.go
index d7ad3079ee..86625846c2 100644
--- a/lxd/network/openvswitch/ovn.go
+++ b/lxd/network/openvswitch/ovn.go
@@ -125,8 +125,14 @@ func (o *OVN) nbctl(args ...string) (string, error) {
 }
 
 // LogicalRouterAdd adds a named logical router.
-func (o *OVN) LogicalRouterAdd(routerName OVNRouter) error {
-       _, err := o.nbctl("lr-add", string(routerName))
+func (o *OVN) LogicalRouterAdd(routerName OVNRouter, mayExist bool) error {
+       args := []string{}
+
+       if mayExist {
+               args = append(args, "--may-exist")
+       }
+
+       _, err := o.nbctl(append(args, "lr-add", string(routerName))...)
        if err != nil {
                return err
        }

From a67d1ed533580a1e7829ca9afd7a99363eba4ce4 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Fri, 18 Dec 2020 11:46:13 +0000
Subject: [PATCH 02/16] lxd/network/openvswitch/ovn: Adds mayExist argument to
 LogicalRouterSNATAdd

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

diff --git a/lxd/network/openvswitch/ovn.go b/lxd/network/openvswitch/ovn.go
index 86625846c2..f529d39394 100644
--- a/lxd/network/openvswitch/ovn.go
+++ b/lxd/network/openvswitch/ovn.go
@@ -151,8 +151,14 @@ func (o OVN) LogicalRouterDelete(routerName OVNRouter) 
error {
 }
 
 // LogicalRouterSNATAdd adds an SNAT rule to a logical router to translate 
packets from intNet to extIP.
-func (o *OVN) LogicalRouterSNATAdd(routerName OVNRouter, intNet *net.IPNet, 
extIP net.IP) error {
-       _, err := o.nbctl("lr-nat-add", string(routerName), "snat", 
extIP.String(), intNet.String())
+func (o *OVN) LogicalRouterSNATAdd(routerName OVNRouter, intNet *net.IPNet, 
extIP net.IP, mayExist bool) error {
+       args := []string{}
+
+       if mayExist {
+               args = append(args, "--may-exist")
+       }
+
+       _, err := o.nbctl(append(args, "lr-nat-add", string(routerName), 
"snat", extIP.String(), intNet.String())...)
        if err != nil {
                return err
        }

From 94fdc43a5e424d0a9ec4d35e70f12af2a1a5dd65 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Fri, 18 Dec 2020 11:46:41 +0000
Subject: [PATCH 03/16] lxd/network/openvswitch/ovn: Simplifies
 LogicalRouterRouteAdd

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

diff --git a/lxd/network/openvswitch/ovn.go b/lxd/network/openvswitch/ovn.go
index f529d39394..fa6a409b6c 100644
--- a/lxd/network/openvswitch/ovn.go
+++ b/lxd/network/openvswitch/ovn.go
@@ -204,8 +204,7 @@ func (o *OVN) LogicalRouterRouteAdd(routerName OVNRouter, 
destination *net.IPNet
                args = append(args, "--may-exist")
        }
 
-       args = append(args, "lr-route-add", string(routerName), 
destination.String(), nextHop.String())
-       _, err := o.nbctl(args...)
+       _, err := o.nbctl(append(args, "lr-route-add", string(routerName), 
destination.String(), nextHop.String())...)
        if err != nil {
                return err
        }

From e47204f3c35bc466639591ff1b13d6302e4d6a21 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Fri, 18 Dec 2020 11:46:57 +0000
Subject: [PATCH 04/16] lxd/network/openvswitch/ovn: Adds mayExist argument to
 LogicalRouterPortAdd

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

diff --git a/lxd/network/openvswitch/ovn.go b/lxd/network/openvswitch/ovn.go
index fa6a409b6c..f4bd7f186f 100644
--- a/lxd/network/openvswitch/ovn.go
+++ b/lxd/network/openvswitch/ovn.go
@@ -230,7 +230,29 @@ func (o *OVN) LogicalRouterRouteDelete(routerName 
OVNRouter, destination *net.IP
 }
 
 // LogicalRouterPortAdd adds a named logical router port to a logical router.
-func (o *OVN) LogicalRouterPortAdd(routerName OVNRouter, portName 
OVNRouterPort, mac net.HardwareAddr, ipAddr ...*net.IPNet) error {
+func (o *OVN) LogicalRouterPortAdd(routerName OVNRouter, portName 
OVNRouterPort, mac net.HardwareAddr, ipAddr []*net.IPNet, mayExist bool) error {
+       if mayExist {
+               // Check if it exists and update addresses.
+               _, err := o.nbctl("list", "Logical_Router_Port", 
string(portName))
+               if err == nil {
+                       // Router port exists.
+                       ips := make([]string, 0, len(ipAddr))
+                       for _, ip := range ipAddr {
+                               ips = append(ips, ip.String())
+                       }
+
+                       _, err := o.nbctl("set", "Logical_Router_Port", 
string(portName),
+                               fmt.Sprintf(`networks="%s"`, strings.Join(ips, 
`","`)),
+                               fmt.Sprintf(`mac="%s"`, 
fmt.Sprintf(mac.String())),
+                       )
+                       if err != nil {
+                               return err
+                       }
+
+                       return nil
+               }
+       }
+
        args := []string{"lrp-add", string(routerName), string(portName), 
mac.String()}
        for _, ipNet := range ipAddr {
                args = append(args, ipNet.String())

From 053db5f5a862163d88b494f58a70e61ac3a26cd2 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Fri, 18 Dec 2020 11:47:14 +0000
Subject: [PATCH 05/16] lxd/network/openvswitch/ovn: Adds
 LogicalRouterSNATDeleteAll function

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

diff --git a/lxd/network/openvswitch/ovn.go b/lxd/network/openvswitch/ovn.go
index f4bd7f186f..47b2bdcceb 100644
--- a/lxd/network/openvswitch/ovn.go
+++ b/lxd/network/openvswitch/ovn.go
@@ -166,6 +166,16 @@ func (o *OVN) LogicalRouterSNATAdd(routerName OVNRouter, 
intNet *net.IPNet, extI
        return nil
 }
 
+// LogicalRouterSNATDeleteAll deletes all SNAT rules from a logical router.
+func (o *OVN) LogicalRouterSNATDeleteAll(routerName OVNRouter) error {
+       _, err := o.nbctl("--if-exists", "lr-nat-del", string(routerName), 
"snat")
+       if err != nil {
+               return err
+       }
+
+       return nil
+}
+
 // LogicalRouterDNATSNATAdd adds a DNAT and SNAT rule to a logical router to 
translate packets from extIP to intIP.
 func (o *OVN) LogicalRouterDNATSNATAdd(routerName OVNRouter, extIP net.IP, 
intIP net.IP, stateless bool, mayExist bool) error {
        args := []string{}

From b43527e9d9de2b0ff81d5bfda2fb759be59914e2 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Fri, 18 Dec 2020 16:30:02 +0000
Subject: [PATCH 06/16] lxd/network/openvswitch/ovn: Clear unused keys in
 LogicalSwitchSetIPAllocation

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

diff --git a/lxd/network/openvswitch/ovn.go b/lxd/network/openvswitch/ovn.go
index 47b2bdcceb..01ba706dc4 100644
--- a/lxd/network/openvswitch/ovn.go
+++ b/lxd/network/openvswitch/ovn.go
@@ -383,14 +383,19 @@ func (o *OVN) LogicalSwitchDelete(switchName OVNSwitch) 
error {
 
 // LogicalSwitchSetIPAllocation sets the IP allocation config on the logical 
switch.
 func (o *OVN) LogicalSwitchSetIPAllocation(switchName OVNSwitch, opts 
*OVNIPAllocationOpts) error {
+       var removeOtherConfigKeys []string
        args := []string{"set", "logical_switch", string(switchName)}
 
        if opts.PrefixIPv4 != nil {
                args = append(args, fmt.Sprintf("other_config:subnet=%s", 
opts.PrefixIPv4.String()))
+       } else {
+               removeOtherConfigKeys = append(removeOtherConfigKeys, "subnet")
        }
 
        if opts.PrefixIPv6 != nil {
                args = append(args, fmt.Sprintf("other_config:ipv6_prefix=%s", 
opts.PrefixIPv6.String()))
+       } else {
+               removeOtherConfigKeys = append(removeOtherConfigKeys, 
"ipv6_prefix")
        }
 
        if len(opts.ExcludeIPv4) > 0 {
@@ -406,6 +411,17 @@ func (o *OVN) LogicalSwitchSetIPAllocation(switchName 
OVNSwitch, opts *OVNIPAllo
                }
 
                args = append(args, fmt.Sprintf("other_config:exclude_ips=%s", 
strings.Join(excludeIPs, " ")))
+       } else {
+               removeOtherConfigKeys = append(removeOtherConfigKeys, 
"exclude_ips")
+       }
+
+       // Clear any unused keys first.
+       if len(removeOtherConfigKeys) > 0 {
+               removeArgs := append([]string{"remove", "logical_switch", 
string(switchName), "other_config"}, removeOtherConfigKeys...)
+               _, err := o.nbctl(removeArgs...)
+               if err != nil {
+                       return err
+               }
        }
 
        // Only run command if at least one setting is specified.

From 799ce55e8f6a28d9d3ed09aaaeb93c11003a63f5 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Fri, 18 Dec 2020 17:47:47 +0000
Subject: [PATCH 07/16] lxd/network/openvswitch/ovn: Adds support for clearing
 unused settings in LogicalRouterPortSetIPv6Advertisements

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

diff --git a/lxd/network/openvswitch/ovn.go b/lxd/network/openvswitch/ovn.go
index 01ba706dc4..cbdb7e018e 100644
--- a/lxd/network/openvswitch/ovn.go
+++ b/lxd/network/openvswitch/ovn.go
@@ -290,27 +290,53 @@ func (o *OVN) LogicalRouterPortDelete(portName 
OVNRouterPort) error {
 func (o *OVN) LogicalRouterPortSetIPv6Advertisements(portName OVNRouterPort, 
opts *OVNIPv6RAOpts) error {
        args := []string{"set", "logical_router_port", string(portName),
                fmt.Sprintf("ipv6_ra_configs:send_periodic=%t", 
opts.SendPeriodic),
-               fmt.Sprintf("ipv6_ra_configs:address_mode=%s", 
string(opts.AddressMode)),
+       }
+
+       var removeRAConfigKeys []string
+
+       if opts.AddressMode != "" {
+               args = append(args, 
fmt.Sprintf("ipv6_ra_configs:address_mode=%s", string(opts.AddressMode)))
+       } else {
+               removeRAConfigKeys = append(removeRAConfigKeys, "address_mode")
        }
 
        if opts.MaxInterval > 0 {
                args = append(args, 
fmt.Sprintf("ipv6_ra_configs:max_interval=%d", opts.MaxInterval/time.Second))
+       } else {
+               removeRAConfigKeys = append(removeRAConfigKeys, "max_interval")
        }
 
        if opts.MinInterval > 0 {
                args = append(args, 
fmt.Sprintf("ipv6_ra_configs:min_interval=%d", opts.MinInterval/time.Second))
+       } else {
+               removeRAConfigKeys = append(removeRAConfigKeys, "min_interval")
        }
 
        if opts.MTU > 0 {
                args = append(args, fmt.Sprintf("ipv6_ra_configs:mtu=%d", 
opts.MTU))
+       } else {
+               removeRAConfigKeys = append(removeRAConfigKeys, "mtu")
        }
 
        if len(opts.DNSSearchList) > 0 {
                args = append(args, fmt.Sprintf("ipv6_ra_configs:dnssl=%s", 
strings.Join(opts.DNSSearchList, ",")))
+       } else {
+               removeRAConfigKeys = append(removeRAConfigKeys, "dnssl")
        }
 
        if opts.RecursiveDNSServer != nil {
                args = append(args, fmt.Sprintf("ipv6_ra_configs:rdnss=%s", 
opts.RecursiveDNSServer.String()))
+       } else {
+               removeRAConfigKeys = append(removeRAConfigKeys, "rdnss")
+       }
+
+       // Clear any unused keys first.
+       if len(removeRAConfigKeys) > 0 {
+               removeArgs := append([]string{"remove", "logical_router_port", 
string(portName), "ipv6_ra_configs"}, removeRAConfigKeys...)
+               _, err := o.nbctl(removeArgs...)
+               if err != nil {
+                       return err
+               }
        }
 
        // Configure IPv6 Router Advertisements.

From 0334dfe8dc8dbde5ff9ae9d78c879e9f064209ce Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Fri, 18 Dec 2020 17:48:18 +0000
Subject: [PATCH 08/16] lxd/network/openvswitch/ovn: Adds
 LogicalRouterPortDeleteIPv6Advertisements function

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

diff --git a/lxd/network/openvswitch/ovn.go b/lxd/network/openvswitch/ovn.go
index cbdb7e018e..a263bb5ca4 100644
--- a/lxd/network/openvswitch/ovn.go
+++ b/lxd/network/openvswitch/ovn.go
@@ -348,6 +348,17 @@ func (o *OVN) 
LogicalRouterPortSetIPv6Advertisements(portName OVNRouterPort, opt
        return nil
 }
 
+// LogicalRouterPortDeleteIPv6Advertisements removes the IPv6 RA announcement 
settings from a router port.
+func (o *OVN) LogicalRouterPortDeleteIPv6Advertisements(portName 
OVNRouterPort) error {
+       // Delete IPv6 Router Advertisements.
+       _, err := o.nbctl("clear", "logical_router_port", string(portName), 
"ipv6_ra_configs")
+       if err != nil {
+               return err
+       }
+
+       return nil
+}
+
 // LogicalRouterPortLinkChassisGroup links a logical router port to a HA 
chassis group.
 func (o *OVN) LogicalRouterPortLinkChassisGroup(portName OVNRouterPort, 
haChassisGroupName OVNChassisGroup) error {
        chassisGroupID, err := o.nbctl("--format=csv", "--no-headings", 
"--data=bare", "--colum=_uuid", "find", "ha_chassis_group", 
fmt.Sprintf("name=%s", haChassisGroupName))

From 271257c7c3dc04495a1abfc1ed080ae08840342c Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Fri, 18 Dec 2020 17:52:22 +0000
Subject: [PATCH 09/16] lxd/network/driver/ovn: Enforce that ipv6.address if
 specified is at least a /64 subnet

OVN requires at least a /64 for DHCP and SLAAC.

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

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index db9b7366d2..f9df44f31a 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -205,6 +205,16 @@ func (n *ovn) Validate(config map[string]string) error {
                return err
        }
 
+       // Check that if IPv6 enabled then the network size must be at least a 
/64 as both RA and DHCPv6
+       // in OVN (as it generates addresses using EUI64) require at least a 
/64 subnet to operate.
+       _, ipv6Net, _ := net.ParseCIDR(config["ipv6.address"])
+       if ipv6Net != nil {
+               ones, _ := ipv6Net.Mask.Size()
+               if ones > 64 {
+                       return fmt.Errorf("IPv6 subnet must be at least a /64")
+               }
+       }
+
        // Load the project to get uplink network restrictions.
        p, err := n.state.Cluster.GetProject(n.project)
        if err != nil {

From 87218ebf8aef6308a4cfaf11da4a5fd80e725e63 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Fri, 18 Dec 2020 17:55:01 +0000
Subject: [PATCH 10/16] lxd/network/driver/ovn: Pass update flag to mayExist
 where possible

And don't delete on failure when updating.

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

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index f9df44f31a..f9b5d47573 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -1557,18 +1557,19 @@ func (n *ovn) setup(update bool) error {
                return err
        }
 
-       revert.Add(func() { client.ChassisGroupDelete(n.getChassisGroupName()) 
})
-
-       // Create logical router.
-       if update {
-               client.LogicalRouterDelete(n.getRouterName())
+       if !update {
+               revert.Add(func() { 
client.ChassisGroupDelete(n.getChassisGroupName()) })
        }
 
-       err = client.LogicalRouterAdd(n.getRouterName())
+       // Create logical router.
+       err = client.LogicalRouterAdd(n.getRouterName(), update)
        if err != nil {
                return errors.Wrapf(err, "Failed adding router")
        }
-       revert.Add(func() { client.LogicalRouterDelete(n.getRouterName()) })
+
+       if !update {
+               revert.Add(func() { 
client.LogicalRouterDelete(n.getRouterName()) })
+       }
 
        // Configure logical router.
 
@@ -1589,23 +1590,24 @@ func (n *ovn) setup(update bool) error {
        }
 
        if len(extRouterIPs) > 0 {
-               // Create external logical switch.
-               if update {
-                       client.LogicalSwitchDelete(n.getExtSwitchName())
-               }
-
-               err = client.LogicalSwitchAdd(n.getExtSwitchName(), false)
+               err = client.LogicalSwitchAdd(n.getExtSwitchName(), update)
                if err != nil {
                        return errors.Wrapf(err, "Failed adding external 
switch")
                }
-               revert.Add(func() { 
client.LogicalSwitchDelete(n.getExtSwitchName()) })
+
+               if !update {
+                       revert.Add(func() { 
client.LogicalSwitchDelete(n.getExtSwitchName()) })
+               }
 
                // Create external router port.
-               err = client.LogicalRouterPortAdd(n.getRouterName(), 
n.getRouterExtPortName(), routerMAC, extRouterIPs...)
+               err = client.LogicalRouterPortAdd(n.getRouterName(), 
n.getRouterExtPortName(), routerMAC, extRouterIPs, update)
                if err != nil {
                        return errors.Wrapf(err, "Failed adding external router 
port")
                }
-               revert.Add(func() { 
client.LogicalRouterPortDelete(n.getRouterExtPortName()) })
+
+               if !update {
+                       revert.Add(func() { 
client.LogicalRouterPortDelete(n.getRouterExtPortName()) })
+               }
 
                // Associate external router port to chassis group.
                err = 
client.LogicalRouterPortLinkChassisGroup(n.getRouterExtPortName(), 
n.getChassisGroupName())
@@ -1614,11 +1616,14 @@ func (n *ovn) setup(update bool) error {
                }
 
                // Create external switch port and link to router port.
-               err = client.LogicalSwitchPortAdd(n.getExtSwitchName(), 
n.getExtSwitchRouterPortName(), false)
+               err = client.LogicalSwitchPortAdd(n.getExtSwitchName(), 
n.getExtSwitchRouterPortName(), update)
                if err != nil {
                        return errors.Wrapf(err, "Failed adding external switch 
router port")
                }
-               revert.Add(func() { 
client.LogicalSwitchPortDelete(n.getExtSwitchRouterPortName()) })
+
+               if !update {
+                       revert.Add(func() { 
client.LogicalSwitchPortDelete(n.getExtSwitchRouterPortName()) })
+               }
 
                err = 
client.LogicalSwitchPortLinkRouter(n.getExtSwitchRouterPortName(), 
n.getRouterExtPortName())
                if err != nil {
@@ -1626,11 +1631,14 @@ func (n *ovn) setup(update bool) error {
                }
 
                // Create external switch port and link to external provider 
network.
-               err = client.LogicalSwitchPortAdd(n.getExtSwitchName(), 
n.getExtSwitchProviderPortName(), false)
+               err = client.LogicalSwitchPortAdd(n.getExtSwitchName(), 
n.getExtSwitchProviderPortName(), update)
                if err != nil {
                        return errors.Wrapf(err, "Failed adding external switch 
provider port")
                }
-               revert.Add(func() { 
client.LogicalSwitchPortDelete(n.getExtSwitchProviderPortName()) })
+
+               if !update {
+                       revert.Add(func() { 
client.LogicalSwitchPortDelete(n.getExtSwitchProviderPortName()) })
+               }
 
                err = 
client.LogicalSwitchPortLinkProviderNetwork(n.getExtSwitchProviderPortName(), 
uplinkNet.extSwitchProviderName)
                if err != nil {
@@ -1639,14 +1647,14 @@ func (n *ovn) setup(update bool) error {
 
                // Add SNAT rules.
                if shared.IsTrue(n.config["ipv4.nat"]) && routerIntPortIPv4Net 
!= nil && routerExtPortIPv4 != nil {
-                       err = client.LogicalRouterSNATAdd(n.getRouterName(), 
routerIntPortIPv4Net, routerExtPortIPv4)
+                       err = client.LogicalRouterSNATAdd(n.getRouterName(), 
routerIntPortIPv4Net, routerExtPortIPv4, update)
                        if err != nil {
                                return err
                        }
                }
 
                if shared.IsTrue(n.config["ipv6.nat"]) && routerIntPortIPv6Net 
!= nil && routerExtPortIPv6 != nil {
-                       err = client.LogicalRouterSNATAdd(n.getRouterName(), 
routerIntPortIPv6Net, routerExtPortIPv6)
+                       err = client.LogicalRouterSNATAdd(n.getRouterName(), 
routerIntPortIPv6Net, routerExtPortIPv6, update)
                        if err != nil {
                                return err
                        }
@@ -1673,7 +1681,10 @@ func (n *ovn) setup(update bool) error {
        if err != nil {
                return errors.Wrapf(err, "Failed adding internal switch")
        }
-       revert.Add(func() { client.LogicalSwitchDelete(n.getIntSwitchName()) })
+
+       if !update {
+               revert.Add(func() { 
client.LogicalSwitchDelete(n.getIntSwitchName()) })
+       }
 
        var excludeIPV4 []shared.IPRange
        if routerIntPortIPv4 != nil {
@@ -1821,7 +1832,10 @@ func (n *ovn) setup(update bool) error {
        if err != nil {
                return errors.Wrapf(err, "Failed adding internal switch router 
port")
        }
-       revert.Add(func() { 
client.LogicalSwitchPortDelete(n.getIntSwitchRouterPortName()) })
+
+       if !update {
+               revert.Add(func() { 
client.LogicalSwitchPortDelete(n.getIntSwitchRouterPortName()) })
+       }
 
        err = 
client.LogicalSwitchPortLinkRouter(n.getIntSwitchRouterPortName(), 
n.getRouterIntPortName())
        if err != nil {

From c58ccecb18c04fc973a879989dff9b49c77593ef Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Fri, 18 Dec 2020 17:55:51 +0000
Subject: [PATCH 11/16] lxd/network/driver/ovn: Delete SNAT rules from route
 before adding new ones

This ensures that during an update, old SNAT rules are removed.

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

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index f9b5d47573..e4a39b519b 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -1645,6 +1645,15 @@ func (n *ovn) setup(update bool) error {
                        return errors.Wrapf(err, "Failed linking external 
switch provider port to external provider network")
                }
 
+               // Remove any existing SNAT rules on update. As currently these 
are only defined from the network
+               // config rather than from any instance NIC config, so we can 
re-create the active config below.
+               if update {
+                       err = 
client.LogicalRouterSNATDeleteAll(n.getRouterName())
+                       if err != nil {
+                               return errors.Wrapf(err, "Failed removing 
existing router SNAT rules")
+                       }
+               }
+
                // Add SNAT rules.
                if shared.IsTrue(n.config["ipv4.nat"]) && routerIntPortIPv4Net 
!= nil && routerExtPortIPv4 != nil {
                        err = client.LogicalRouterSNATAdd(n.getRouterName(), 
routerIntPortIPv4Net, routerExtPortIPv4, update)

From cb9db30dbd7848b2f4c14ce636ab4bbb9c5e2f51 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Fri, 18 Dec 2020 17:56:24 +0000
Subject: [PATCH 12/16] lxd/network/driver/ovn: Improve SNAT failure errors

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 e4a39b519b..1c0439f3a1 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -1658,14 +1658,14 @@ func (n *ovn) setup(update bool) error {
                if shared.IsTrue(n.config["ipv4.nat"]) && routerIntPortIPv4Net 
!= nil && routerExtPortIPv4 != nil {
                        err = client.LogicalRouterSNATAdd(n.getRouterName(), 
routerIntPortIPv4Net, routerExtPortIPv4, update)
                        if err != nil {
-                               return err
+                               return errors.Wrapf(err, "Failed adding router 
IPv4 SNAT rule")
                        }
                }
 
                if shared.IsTrue(n.config["ipv6.nat"]) && routerIntPortIPv6Net 
!= nil && routerExtPortIPv6 != nil {
                        err = client.LogicalRouterSNATAdd(n.getRouterName(), 
routerIntPortIPv6Net, routerExtPortIPv6, update)
                        if err != nil {
-                               return err
+                               return errors.Wrapf(err, "Failed adding router 
IPv6 SNAT rule")
                        }
                }
 

From 0455da20308446fa359d010aee1b301f58e6f14d Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Fri, 18 Dec 2020 17:57:00 +0000
Subject: [PATCH 13/16] lxd/network/driver/ovn: Pass update to mayExists when
 setting up default routes

And remove default routes when IP protocol address isn't enabled.

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

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index 1c0439f3a1..f386d3a6c9 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -1669,19 +1669,31 @@ func (n *ovn) setup(update bool) error {
                        }
                }
 
-               // Add default routes.
+               // Add or remove default routes as config dictates.
+               defaultIPv4Route := &net.IPNet{IP: net.IPv4zero, Mask: 
net.CIDRMask(0, 32)}
                if uplinkNet.routerExtGwIPv4 != nil {
-                       err = client.LogicalRouterRouteAdd(n.getRouterName(), 
&net.IPNet{IP: net.IPv4zero, Mask: net.CIDRMask(0, 32)}, 
uplinkNet.routerExtGwIPv4, false)
+                       err = client.LogicalRouterRouteAdd(n.getRouterName(), 
defaultIPv4Route, uplinkNet.routerExtGwIPv4, update)
                        if err != nil {
                                return errors.Wrapf(err, "Failed adding IPv4 
default route")
                        }
+               } else if update {
+                       err = 
client.LogicalRouterRouteDelete(n.getRouterName(), defaultIPv4Route, nil)
+                       if err != nil {
+                               return errors.Wrapf(err, "Failed removing IPv4 
default route")
+                       }
                }
 
+               defaultIPv6Route := &net.IPNet{IP: net.IPv6zero, Mask: 
net.CIDRMask(0, 128)}
                if uplinkNet.routerExtGwIPv6 != nil {
-                       err = client.LogicalRouterRouteAdd(n.getRouterName(), 
&net.IPNet{IP: net.IPv6zero, Mask: net.CIDRMask(0, 128)}, 
uplinkNet.routerExtGwIPv6, false)
+                       err = client.LogicalRouterRouteAdd(n.getRouterName(), 
defaultIPv6Route, uplinkNet.routerExtGwIPv6, update)
                        if err != nil {
                                return errors.Wrapf(err, "Failed adding IPv6 
default route")
                        }
+               } else if update {
+                       err = 
client.LogicalRouterRouteDelete(n.getRouterName(), defaultIPv6Route, nil)
+                       if err != nil {
+                               return errors.Wrapf(err, "Failed removing IPv6 
default route")
+                       }
                }
        }
 

From acf0f3da29e98fb94c67636786a1e395c35b454a Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Fri, 18 Dec 2020 17:58:20 +0000
Subject: [PATCH 14/16] lxd/network/driver/ovn: Create internal router port
 before DHCP option setup

Keeps DHCP setup steps together.

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

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index f386d3a6c9..6ce441e752 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -1722,6 +1722,38 @@ func (n *ovn) setup(update bool) error {
                return errors.Wrapf(err, "Failed setting IP allocation settings 
on internal switch")
        }
 
+       // Gather internal router port IPs (in CIDR format).
+       intRouterIPs := []*net.IPNet{}
+
+       if routerIntPortIPv4Net != nil {
+               intRouterIPs = append(intRouterIPs, &net.IPNet{
+                       IP:   routerIntPortIPv4,
+                       Mask: routerIntPortIPv4Net.Mask,
+               })
+       }
+
+       if routerIntPortIPv6Net != nil {
+               intRouterIPs = append(intRouterIPs, &net.IPNet{
+                       IP:   routerIntPortIPv6,
+                       Mask: routerIntPortIPv6Net.Mask,
+               })
+       }
+
+       if len(intRouterIPs) <= 0 {
+               return fmt.Errorf("No internal IPs defined for network router")
+       }
+
+       // Create internal router port.
+       err = client.LogicalRouterPortAdd(n.getRouterName(), 
n.getRouterIntPortName(), routerMAC, intRouterIPs, update)
+       if err != nil {
+               return errors.Wrapf(err, "Failed adding internal router port")
+       }
+
+       if !update {
+               revert.Add(func() { 
client.LogicalRouterPortDelete(n.getRouterIntPortName()) })
+       }
+
+       // Configure DHCP option sets.
        var dhcpv4UUID, dhcpv6UUID string
        dhcpV4Subnet := n.DHCPv4Subnet()
        dhcpV6Subnet := n.DHCPv6Subnet()
@@ -1763,27 +1795,6 @@ func (n *ovn) setup(update bool) error {
                }
        }
 
-       // Internal router port IPs (in CIDR format).
-       intRouterIPs := []*net.IPNet{}
-
-       if routerIntPortIPv4Net != nil {
-               intRouterIPs = append(intRouterIPs, &net.IPNet{
-                       IP:   routerIntPortIPv4,
-                       Mask: routerIntPortIPv4Net.Mask,
-               })
-       }
-
-       if routerIntPortIPv6Net != nil {
-               intRouterIPs = append(intRouterIPs, &net.IPNet{
-                       IP:   routerIntPortIPv6,
-                       Mask: routerIntPortIPv6Net.Mask,
-               })
-       }
-
-       if len(intRouterIPs) <= 0 {
-               return fmt.Errorf("No IPs defined for network router")
-       }
-
        // Create DHCPv4 options for internal switch.
        if dhcpV4Subnet != nil {
                err = 
client.LogicalSwitchDHCPv4OptionsSet(n.getIntSwitchName(), dhcpv4UUID, 
dhcpV4Subnet, &openvswitch.OVNDHCPv4Opts{
@@ -1812,13 +1823,6 @@ func (n *ovn) setup(update bool) error {
                }
        }
 
-       // Create internal router port.
-       err = client.LogicalRouterPortAdd(n.getRouterName(), 
n.getRouterIntPortName(), routerMAC, intRouterIPs...)
-       if err != nil {
-               return errors.Wrapf(err, "Failed adding internal router port")
-       }
-       revert.Add(func() { 
client.LogicalRouterPortDelete(n.getRouterIntPortName()) })
-
        // Set IPv6 router advertisement settings.
        if dhcpV6Subnet != nil {
                adressMode := openvswitch.OVNIPv6AddressModeSLAAC

From 2b3cf479b84ce250ea8b90890fb5ade64e3ff1db Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Fri, 18 Dec 2020 18:12:33 +0000
Subject: [PATCH 15/16] lxd/network/driver/ovn: Modifies IPv6 RA settings and
 removes them entirely when IPv6 disabled

Settings change to:

 - ipv6.dhcp = false
        RA with slaac
 - ipv6.dhcp = true
        RA with stateless DHCP
 - ipv6.dhcp = true and ipv6.dhcp.stateful = true
        RA with stateful DHCP

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

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index 6ce441e752..630ae3c914 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -1824,10 +1824,13 @@ func (n *ovn) setup(update bool) error {
        }
 
        // Set IPv6 router advertisement settings.
-       if dhcpV6Subnet != nil {
+       if routerIntPortIPv6Net != nil {
                adressMode := openvswitch.OVNIPv6AddressModeSLAAC
-               if shared.IsTrue(n.config["ipv6.dhcp.stateful"]) {
-                       adressMode = openvswitch.OVNIPv6AddressModeDHCPStateful
+               if dhcpV6Subnet != nil {
+                       adressMode = openvswitch.OVNIPv6AddressModeDHCPStateless
+                       if shared.IsTrue(n.config["ipv6.dhcp.stateful"]) {
+                               adressMode = 
openvswitch.OVNIPv6AddressModeDHCPStateful
+                       }
                }
 
                var recursiveDNSServer net.IP
@@ -1850,6 +1853,11 @@ func (n *ovn) setup(update bool) error {
                if err != nil {
                        return errors.Wrapf(err, "Failed setting internal 
router port IPv6 advertisement settings")
                }
+       } else {
+               err = 
client.LogicalRouterPortDeleteIPv6Advertisements(n.getRouterIntPortName())
+               if err != nil {
+                       return errors.Wrapf(err, "Failed removing internal 
router port IPv6 advertisement settings")
+               }
        }
 
        // Create internal switch port and link to router port.

From f486c839c6f9cc884ba40d3cc67031aab69dbe0f Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Fri, 18 Dec 2020 18:14:07 +0000
Subject: [PATCH 16/16] lxd/network/driver/ovn: Don't return DHCPv6 subnet if
 IPv6 prefix smaller than /64

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

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index 630ae3c914..6f2d9d7d39 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -2585,6 +2585,11 @@ func (n *ovn) DHCPv6Subnet() *net.IPNet {
                return nil
        }
 
+       ones, _ := subnet.Mask.Size()
+       if ones > 64 {
+               return nil // OVN only supports DHCPv6 allocated using EUI64 
(which requires at least a /64).
+       }
+
        return subnet
 }
 
_______________________________________________
lxc-devel mailing list
lxc-devel@lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to