[lxc-devel] [lxd/master] Network: Adds ipv4.dhcp and ipv6.dhcp settings for OVN networks

tomponline on Github lxc-bot at linuxcontainers.org
Wed Dec 9 14:15:13 UTC 2020


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 335 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20201209/67f82fbe/attachment.bin>
-------------- next part --------------
From 19b1f4ea200277bbcb3519e4ec630bee769b7156 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 9 Dec 2020 13:58:55 +0000
Subject: [PATCH 1/7] lxd/network/openvswitch/ovn: Exports
 LogicalSwitchDHCPOptionsDelete and adds optional UUID filter for deletion

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

diff --git a/lxd/network/openvswitch/ovn.go b/lxd/network/openvswitch/ovn.go
index 643e6d7047..d7ad3079ee 100644
--- a/lxd/network/openvswitch/ovn.go
+++ b/lxd/network/openvswitch/ovn.go
@@ -325,7 +325,7 @@ func (o *OVN) LogicalSwitchDelete(switchName OVNSwitch) error {
 		return err
 	}
 
-	err = o.logicalSwitchDHCPOptionsDelete(switchName)
+	err = o.LogicalSwitchDHCPOptionsDelete(switchName)
 	if err != nil {
 		return err
 	}
@@ -549,8 +549,9 @@ func (o *OVN) LogicalSwitchDHCPOptionsGet(switchName OVNSwitch) ([]OVNDHCPOptsSe
 	return dhcpOpts, nil
 }
 
-// logicalSwitchDHCPOptionsDelete deletes any DHCP options defined for a switch.
-func (o *OVN) logicalSwitchDHCPOptionsDelete(switchName OVNSwitch) error {
+// LogicalSwitchDHCPOptionsDelete deletes any DHCP options defined for a switch.
+// Optionally accepts one or more specific UUID records to delete (if they are associated to the specified switch).
+func (o *OVN) LogicalSwitchDHCPOptionsDelete(switchName OVNSwitch, onlyUUID ...string) error {
 	existingOpts, err := o.nbctl("--format=csv", "--no-headings", "--data=bare", "--colum=_uuid", "find", "dhcp_options",
 		fmt.Sprintf("external_ids:lxd_switch=%s", string(switchName)),
 	)
@@ -558,12 +559,28 @@ func (o *OVN) logicalSwitchDHCPOptionsDelete(switchName OVNSwitch) error {
 		return err
 	}
 
+	shouldDelete := func(existingUUID string) bool {
+		if len(onlyUUID) <= 0 {
+			return true // Delete all records if no UUID filter supplied.
+		}
+
+		for _, uuid := range onlyUUID {
+			if existingUUID == uuid {
+				return true
+			}
+		}
+
+		return false
+	}
+
 	existingOpts = strings.TrimSpace(existingOpts)
 	if existingOpts != "" {
 		for _, uuid := range strings.Split(existingOpts, "\n") {
-			_, err = o.nbctl("destroy", "dhcp_options", uuid)
-			if err != nil {
-				return err
+			if shouldDelete(uuid) {
+				_, err = o.nbctl("destroy", "dhcp_options", uuid)
+				if err != nil {
+					return err
+				}
 			}
 		}
 	}

From f893eb22808c247ac88fe0051bc53b91fc66dae8 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 9 Dec 2020 14:02:45 +0000
Subject: [PATCH 2/7] lxc/network/driver/ovn: Adds ipv4.dhcp and ipv6.dhcp
 boolean settings

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

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index dc7668e853..4723a8445f 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -180,6 +180,7 @@ func (n *ovn) Validate(config map[string]string) error {
 
 			return validate.Optional(validate.IsNetworkAddressCIDRV4)(value)
 		},
+		"ipv4.dhcp": validate.Optional(validate.IsBool),
 		"ipv6.address": func(value string) error {
 			if validate.IsOneOf(value, []string{"none", "auto"}) == nil {
 				return nil
@@ -187,6 +188,7 @@ func (n *ovn) Validate(config map[string]string) error {
 
 			return validate.Optional(validate.IsNetworkAddressCIDRV6)(value)
 		},
+		"ipv6.dhcp":          validate.Optional(validate.IsBool),
 		"ipv6.dhcp.stateful": validate.Optional(validate.IsBool),
 		"ipv4.nat":           validate.Optional(validate.IsBool),
 		"ipv6.nat":           validate.Optional(validate.IsBool),

From 9067bf591633532827a2708cccdc3d779f92c7cc Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 9 Dec 2020 14:08:08 +0000
Subject: [PATCH 3/7] lxc/network/driver/ovn: Modifies setup to only activate
 DHCP/RA if its enabled on network

Also removes old DHCP option records if DHCP is being disabled.

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

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index 4723a8445f..d0c35a2a0d 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -1605,6 +1605,8 @@ func (n *ovn) setup(update bool) error {
 	}
 
 	var dhcpv4UUID, dhcpv6UUID string
+	dhcpV4Subnet := n.DHCPv4Subnet()
+	dhcpV6Subnet := n.DHCPv6Subnet()
 
 	if update {
 		// Find first existing DHCP options set for IPv4 and IPv6 and update them instead of adding sets.
@@ -1613,25 +1615,60 @@ func (n *ovn) setup(update bool) error {
 			return errors.Wrapf(err, "Failed getting existing DHCP settings for internal switch")
 		}
 
+		var deleteDHCPRecords []string // DHCP option records to delete if DHCP is being disabled.
+
 		for _, existingOpt := range existingOpts {
 			if existingOpt.CIDR.IP.To4() == nil {
 				if dhcpv6UUID == "" {
 					dhcpv6UUID = existingOpt.UUID
+
+					if dhcpV6Subnet == nil {
+						deleteDHCPRecords = append(deleteDHCPRecords, dhcpv6UUID)
+					}
 				}
 			} else {
 				if dhcpv4UUID == "" {
 					dhcpv4UUID = existingOpt.UUID
+
+					if dhcpV4Subnet == nil {
+						deleteDHCPRecords = append(deleteDHCPRecords, dhcpv4UUID)
+					}
 				}
 			}
 		}
+
+		if len(deleteDHCPRecords) > 0 {
+			err = client.LogicalSwitchDHCPOptionsDelete(n.getIntSwitchName(), deleteDHCPRecords...)
+			if err != nil {
+				return errors.Wrapf(err, "Failed deleting existing DHCP settings for internal switch")
+			}
+		}
 	}
 
 	// Internal router port IPs (in CIDR format).
 	intRouterIPs := []*net.IPNet{}
 
-	// Create DHCPv4 options for internal switch.
 	if routerIntPortIPv4Net != nil {
-		err = client.LogicalSwitchDHCPv4OptionsSet(n.getIntSwitchName(), dhcpv4UUID, routerIntPortIPv4Net, &openvswitch.OVNDHCPv4Opts{
+		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{
 			ServerID:           routerIntPortIPv4,
 			ServerMAC:          routerMAC,
 			Router:             routerIntPortIPv4,
@@ -1643,16 +1680,11 @@ func (n *ovn) setup(update bool) error {
 		if err != nil {
 			return errors.Wrapf(err, "Failed adding DHCPv4 settings for internal switch")
 		}
-
-		intRouterIPs = append(intRouterIPs, &net.IPNet{
-			IP:   routerIntPortIPv4,
-			Mask: routerIntPortIPv4Net.Mask,
-		})
 	}
 
 	// Create DHCPv6 options for internal switch.
-	if routerIntPortIPv6Net != nil {
-		err = client.LogicalSwitchDHCPv6OptionsSet(n.getIntSwitchName(), dhcpv6UUID, routerIntPortIPv6Net, &openvswitch.OVNDHCPv6Opts{
+	if dhcpV6Subnet != nil {
+		err = client.LogicalSwitchDHCPv6OptionsSet(n.getIntSwitchName(), dhcpv6UUID, dhcpV6Subnet, &openvswitch.OVNDHCPv6Opts{
 			ServerID:           routerMAC,
 			RecursiveDNSServer: uplinkNet.dnsIPv6,
 			DNSSearchList:      n.getDNSSearchList(),
@@ -1660,18 +1692,9 @@ func (n *ovn) setup(update bool) error {
 		if err != nil {
 			return errors.Wrapf(err, "Failed adding DHCPv6 settings for internal switch")
 		}
-
-		intRouterIPs = append(intRouterIPs, &net.IPNet{
-			IP:   routerIntPortIPv6,
-			Mask: routerIntPortIPv6Net.Mask,
-		})
 	}
 
 	// Create internal router port.
-	if len(intRouterIPs) <= 0 {
-		return fmt.Errorf("No IPs defined for network router")
-	}
-
 	err = client.LogicalRouterPortAdd(n.getRouterName(), n.getRouterIntPortName(), routerMAC, intRouterIPs...)
 	if err != nil {
 		return errors.Wrapf(err, "Failed adding internal router port")
@@ -1679,7 +1702,7 @@ func (n *ovn) setup(update bool) error {
 	revert.Add(func() { client.LogicalRouterPortDelete(n.getRouterIntPortName()) })
 
 	// Set IPv6 router advertisement settings.
-	if routerIntPortIPv6Net != nil {
+	if dhcpV6Subnet != nil {
 		adressMode := openvswitch.OVNIPv6AddressModeSLAAC
 		if shared.IsTrue(n.config["ipv6.dhcp.stateful"]) {
 			adressMode = openvswitch.OVNIPv6AddressModeDHCPStateful

From 1ba16cffc87d151eeece2fcacd548f09f8fdddb3 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 9 Dec 2020 14:08:59 +0000
Subject: [PATCH 4/7] lxd/network/driver/ovn: Updates InstanceDevicePortAdd to
 respect DHCP options on network

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

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index d0c35a2a0d..d467867c10 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -2118,26 +2118,19 @@ func (n *ovn) InstanceDevicePortAdd(instanceUUID string, instanceName string, de
 		return "", errors.Wrapf(err, "Failed to load uplink network %q", n.config["network"])
 	}
 
-	// Get DHCP options IDs.
-	if validate.IsOneOf(n.getRouterIntPortIPv4Net(), []string{"none", ""}) != nil {
-		_, routerIntPortIPv4Net, err := net.ParseCIDR(n.getRouterIntPortIPv4Net())
-		if err != nil {
-			return "", err
-		}
+	dhcpv4Subnet := n.DHCPv4Subnet()
+	dhcpv6Subnet := n.DHCPv6Subnet()
 
-		dhcpV4ID, err = client.LogicalSwitchDHCPOptionsGetID(n.getIntSwitchName(), routerIntPortIPv4Net)
+	// Get DHCP options IDs.
+	if dhcpv4Subnet != nil {
+		dhcpV4ID, err = client.LogicalSwitchDHCPOptionsGetID(n.getIntSwitchName(), dhcpv4Subnet)
 		if err != nil {
 			return "", err
 		}
 	}
 
-	if validate.IsOneOf(n.getRouterIntPortIPv6Net(), []string{"none", ""}) != nil {
-		_, routerIntPortIPv6Net, err := net.ParseCIDR(n.getRouterIntPortIPv6Net())
-		if err != nil {
-			return "", err
-		}
-
-		dhcpv6ID, err = client.LogicalSwitchDHCPOptionsGetID(n.getIntSwitchName(), routerIntPortIPv6Net)
+	if dhcpv6Subnet != nil {
+		dhcpv6ID, err = client.LogicalSwitchDHCPOptionsGetID(n.getIntSwitchName(), dhcpv6Subnet)
 		if err != nil {
 			return "", err
 		}
@@ -2156,7 +2149,7 @@ func (n *ovn) InstanceDevicePortAdd(instanceUUID string, instanceName string, de
 			}
 
 			if !hasIPv6 {
-				eui64IP, err := eui64.ParseMAC(routerIntPortIPv6Net.IP, mac)
+				eui64IP, err := eui64.ParseMAC(dhcpv6Subnet.IP, mac)
 				if err != nil {
 					return "", errors.Wrapf(err, "Failed generating EUI64 for instance port %q", mac.String())
 				}

From 33a6d4d8a6aac553bfbb7bbf15ad450e186fed3b Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 9 Dec 2020 14:09:24 +0000
Subject: [PATCH 5/7] lxd/network/driver/ovn: Updates DHCPv4Subnet and
 DHCPv6Subnet to use IP helper functions

Signed-off-by: Thomas Parrott <thomas.parrott at 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 d467867c10..280482c4ac 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -2411,7 +2411,7 @@ func (n *ovn) DHCPv4Subnet() *net.IPNet {
 		return nil
 	}
 
-	_, subnet, err := net.ParseCIDR(n.config["ipv4.address"])
+	_, subnet, err := net.ParseCIDR(n.getRouterIntPortIPv4Net())
 	if err != nil {
 		return nil
 	}
@@ -2426,7 +2426,7 @@ func (n *ovn) DHCPv6Subnet() *net.IPNet {
 		return nil
 	}
 
-	_, subnet, err := net.ParseCIDR(n.config["ipv6.address"])
+	_, subnet, err := net.ParseCIDR(n.getRouterIntPortIPv6Net())
 	if err != nil {
 		return nil
 	}

From 0b8ae947101f75bbd1d7423cbf7e2c8c8707bb21 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 9 Dec 2020 14:12:14 +0000
Subject: [PATCH 6/7] api: Adds network_ovn_dhcp extension

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 doc/api-extensions.md | 5 +++++
 shared/version/api.go | 1 +
 2 files changed, 6 insertions(+)

diff --git a/doc/api-extensions.md b/doc/api-extensions.md
index c1645d4f86..92c63f2199 100644
--- a/doc/api-extensions.md
+++ b/doc/api-extensions.md
@@ -1242,3 +1242,8 @@ Adds `ovn.ingress_mode` setting for `physical` networks.
 Sets the method that OVN NIC external IPs will be advertised on uplink network.
 
 Either `l2proxy` (proxy ARP/NDP) or `routed`.
+
+## network\_ovn\_dhcp
+Adds `ipv4.dhcp` and `ipv6.dhcp` settings for `ovn` networks.
+
+Allows DHCP (and RA for IPv6) to be disabled. Defaults to on.
diff --git a/shared/version/api.go b/shared/version/api.go
index ee1ee5a6a8..2c8b4d2176 100644
--- a/shared/version/api.go
+++ b/shared/version/api.go
@@ -240,6 +240,7 @@ var APIExtensions = []string{
 	"resources_network_usb",
 	"resources_disk_address",
 	"network_physical_ovn_ingress_mode",
+	"network_ovn_dhcp",
 }
 
 // APIExtensionsCount returns the number of available API extensions.

From 567f795f599a26b30f5e06e43bc661a0152dbed9 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 9 Dec 2020 14:13:18 +0000
Subject: [PATCH 7/7] doc/networks: Adds ipv4.dhcp and ipv6.dhcp docs for OVN
 networks

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

diff --git a/doc/networks.md b/doc/networks.md
index cd06506e85..c0ead486b3 100644
--- a/doc/networks.md
+++ b/doc/networks.md
@@ -297,8 +297,10 @@ bridge.mtu                      | integer   | -                     | 1442
 dns.domain                      | string    | -                     | lxd                       | Domain to advertise to DHCP clients and use for DNS resolution
 dns.search                      | string    | -                     | -                         | Full comma separated domain search list, defaulting to `dns.domain` value
 ipv4.address                    | string    | standard mode         | auto (on create only)     | IPv4 address for the bridge (CIDR notation). Use "none" to turn off IPv4 or "auto" to generate a new random unused subnet
+ipv4.dhcp                       | boolean   | ipv4 address          | true                      | Whether to allocate addresses using DHCP
 ipv4.nat                        | boolean   | ipv4 address          | false                     | Whether to NAT (will default to true if unset and a random ipv4.address is generated)
 ipv6.address                    | string    | standard mode         | auto (on create only)     | IPv6 address for the bridge (CIDR notation). Use "none" to turn off IPv6 or "auto" to generate a new random unused subnet
+ipv6.dhcp                       | boolean   | ipv6 address          | true                      | Whether to provide additional network configuration over DHCP
 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)
 network                         | string    | -                     | -                         | Uplink network to use for external network access


More information about the lxc-devel mailing list