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

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) ===
Adds support for Instance port DNS records.

Includes https://github.com/lxc/lxd/pull/7789
From fb50fc4a964a1bfdd46b1abff82251a17c9fe0dc Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 19 Aug 2020 09:24:26 +0100
Subject: [PATCH 1/8] doc/networks: Adds link to OVN network

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

diff --git a/doc/networks.md b/doc/networks.md
index 2887aad829..c44dca2abf 100644
--- a/doc/networks.md
+++ b/doc/networks.md
@@ -5,6 +5,7 @@ LXD supports the following network types:
  - [bridge](#network-bridge): Creates an L2 bridge for connecting instances to 
(can provide local DHCP and DNS). This is the default.
  - [macvlan](#network-macvlan): Provides preset configuration to use when 
connecting instances to a parent macvlan interface.
  - [sriov](#network-sriov): Provides preset configuration to use when 
connecting instances to a parent SR-IOV interface.
+ - [ovn](#network-ovn): Creates a logical network using the OVN software 
defined networking system.
 
 The desired type can be specified using the `--type` argument, e.g.
 

From 5398036a31ed17b0a31b13c60454ea80c698a72d Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 19 Aug 2020 09:34:48 +0100
Subject: [PATCH 2/8] lxd/network/network/utils: Adds pingIP function

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

diff --git a/lxd/network/network_utils.go b/lxd/network/network_utils.go
index c8d05367e7..b4757742d3 100644
--- a/lxd/network/network_utils.go
+++ b/lxd/network/network_utils.go
@@ -543,6 +543,22 @@ func inRoutingTable(subnet *net.IPNet) bool {
        return false
 }
 
+// pingIP sends a single ping packet to the specified IP, returns true if 
responds, false if not.
+func pingIP(ip net.IP) bool {
+       cmd := "ping"
+       if ip.To4() == nil {
+               cmd = "ping6"
+       }
+
+       _, err := shared.RunCommand(cmd, "-n", "-q", ip.String(), "-c", "1", 
"-W", "1")
+       if err != nil {
+               // Remote didn't answer.
+               return false
+       }
+
+       return true
+}
+
 func pingSubnet(subnet *net.IPNet) bool {
        var fail bool
        var failLock sync.Mutex
@@ -551,14 +567,7 @@ func pingSubnet(subnet *net.IPNet) bool {
        ping := func(ip net.IP) {
                defer wgChecks.Done()
 
-               cmd := "ping"
-               if ip.To4() == nil {
-                       cmd = "ping6"
-               }
-
-               _, err := shared.RunCommand(cmd, "-n", "-q", ip.String(), "-c", 
"1", "-W", "1")
-               if err != nil {
-                       // Remote didn't answer
+               if !pingIP(ip) {
                        return
                }
 

From 522cedb58fd94649606a888b2e7e5be8c17a3c9e Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 19 Aug 2020 10:07:12 +0100
Subject: [PATCH 3/8] lxd/network/driver/ovn: Pings OVN external IPv6 router IP
 on bridge port start

This is to workaround an issue in older versions of OVN where the router will 
not perform neighbour discovery for the IPv6 gateway when using IPv6 SNAT.

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

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index bf7f12b5d8..2beb096fd9 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -581,6 +581,31 @@ func (n *ovn) startParentPortBridge(parentNet Network) 
error {
                return errors.Wrapf(err, "Failed to associate parent OVS bridge 
%q to OVN provider %q", vars.ovsBridge, parentNet.Name())
        }
 
+       routerExtPortIPv6 := net.ParseIP(n.config[ovnVolatileParentIPv6])
+       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
+               // 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
+               // which will cause the OVN router to learn its MAC address.
+               func() {
+                       // Try several attempts as it can take a few seconds 
for the network to come up.
+                       for i := 0; i < 5; i++ {
+                               if pingIP(routerExtPortIPv6) {
+                                       n.logger.Debug("OVN router external 
IPv6 address reachable", log.Ctx{"ip": routerExtPortIPv6.String()})
+                                       return
+                               }
+
+                               time.Sleep(time.Second)
+                       }
+
+                       n.logger.Warn("OVN router external IPv6 address 
unreachable", log.Ctx{"ip": routerExtPortIPv6.String()})
+               }()
+       }
+
        revert.Success()
        return nil
 }

From 8b539a5a09ea25db45e875468215786f4c59462e Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 19 Aug 2020 12:40:21 +0100
Subject: [PATCH 4/8] lxd/network/openvswitch/dns: Adds LogicalSwitchPortSetDNS
 and LogicalSwitchPortDeleteDNS functions

Populates switch port's DNS records based on port's assigned static and dynamic 
IPs.

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

diff --git a/lxd/network/openvswitch/ovn.go b/lxd/network/openvswitch/ovn.go
index 656ada439d..aab9fb1980 100644
--- a/lxd/network/openvswitch/ovn.go
+++ b/lxd/network/openvswitch/ovn.go
@@ -3,6 +3,7 @@ package openvswitch
 import (
        "fmt"
        "net"
+       "strconv"
        "strings"
        "time"
 
@@ -542,6 +543,152 @@ func (o *OVN) LogicalSwitchPortSet(portName 
OVNSwitchPort, opts *OVNSwitchPortOp
        return nil
 }
 
+// LogicalSwitchPortSetDNS sets up the switch DNS records for the DNS name 
resolving to the IPs of the switch port.
+// Attempts to find at most one IP for each IP protocol, preferring static 
addresses over dynamic.
+func (o *OVN) LogicalSwitchPortSetDNS(switchName OVNSwitch, portName 
OVNSwitchPort, dnsName string) error {
+       var dnsIPv4, dnsIPv6 net.IP
+
+       // parseAndStoreIP checks if the supplied IP string is valid and can be 
used for a missing DNS IP variable.
+       // If the found IP is needed, stores into the relevant dnsIPvP{X} 
variable and returns true.
+       parseAndStoreIP := func(ipRaw string) bool {
+               ip := net.ParseIP(ipRaw)
+               if ip != nil {
+                       isV4 := ip.To4() != nil
+                       if dnsIPv4 == nil && isV4 {
+                               dnsIPv4 = ip
+                               return true
+                       } else if dnsIPv6 == nil && !isV4 {
+                               dnsIPv6 = ip
+                               return true
+                       }
+               }
+
+               return false
+       }
+
+       // Get static and dynamic IPs for switch port.
+       staticAddressesRaw, err := o.nbctl("lsp-get-addresses", 
string(portName))
+       if err != nil {
+               return err
+       }
+
+       staticAddresses := strings.Split(strings.TrimSpace(staticAddressesRaw), 
" ")
+       hasDynamic := false
+       for _, staticAddress := range staticAddresses {
+               // Record that there should be at least one dynamic address 
(may be a MAC address though).
+               if staticAddress == "dynamic" {
+                       hasDynamic = true
+                       continue
+               }
+
+               // Try and find the first IPv4 and IPv6 addresses from the 
static address list.
+               if parseAndStoreIP(staticAddress) {
+                       if dnsIPv4 != nil && dnsIPv6 != nil {
+                               break // We've found all we wanted.
+                       }
+               }
+       }
+
+       // Get dynamic IPs for switch port if indicated and needed.
+       if hasDynamic && (dnsIPv4 == nil || dnsIPv6 == nil) {
+               dynamicAddressesRaw, err := o.nbctl("get", 
"logical_switch_port", string(portName), "dynamic_addresses")
+               if err != nil {
+                       return err
+               }
+
+               dynamicAddressesRaw, err = 
strconv.Unquote(strings.TrimSpace(dynamicAddressesRaw))
+               if err != nil {
+                       return err
+               }
+
+               dynamicAddresses := 
strings.Split(strings.TrimSpace(dynamicAddressesRaw), " ")
+               for _, dynamicAddress := range dynamicAddresses {
+                       // Try and find the first IPv4 and IPv6 addresses from 
the dynamic address list.
+                       if parseAndStoreIP(dynamicAddress) {
+                               if dnsIPv4 != nil && dnsIPv6 != nil {
+                                       break // We've found all we wanted.
+                               }
+                       }
+               }
+       }
+
+       // Create a list of IPs for the DNS record.
+       dnsIPs := make([]string, 0, 2)
+       if dnsIPv4 != nil {
+               dnsIPs = append(dnsIPs, dnsIPv4.String())
+       }
+
+       if dnsIPv6 != nil {
+               dnsIPs = append(dnsIPs, dnsIPv6.String())
+       }
+
+       // Check if existing DNS record exists for switch port.
+       dnsUUID, err := o.nbctl("--format=csv", "--no-headings", "--data=bare", 
"--colum=_uuid", "find", "dns",
+               fmt.Sprintf("external_ids:lxd_switch_port=%s", 
string(portName)),
+       )
+       if err != nil {
+               return err
+       }
+
+       cmdArgs := []string{
+               fmt.Sprintf(`records={"%s"="%s"}`, dnsName, 
strings.Join(dnsIPs, " ")),
+               fmt.Sprintf("external_ids:lxd_switch=%s", string(switchName)),
+               fmt.Sprintf("external_ids:lxd_switch_port=%s", 
string(portName)),
+       }
+
+       dnsUUID = strings.TrimSpace(dnsUUID)
+       if dnsUUID != "" {
+               // Update existing record if exists.
+               _, err = o.nbctl(append([]string{"set", "dns", dnsUUID}, 
cmdArgs...)...)
+               if err != nil {
+                       return err
+               }
+       } else {
+               // Create new record if needed.
+               dnsUUID, err = o.nbctl(append([]string{"create", "dns"}, 
cmdArgs...)...)
+               if err != nil {
+                       return err
+               }
+               dnsUUID = strings.TrimSpace(dnsUUID)
+       }
+
+       // Add DNS record to switch DNS records.
+       _, err = o.nbctl("add", "logical_switch", string(switchName), 
"dns_records", dnsUUID)
+       if err != nil {
+               return err
+       }
+
+       return nil
+}
+
+// LogicalSwitchPortDeleteDNS removes DNS records for a switch port.
+func (o *OVN) LogicalSwitchPortDeleteDNS(switchName OVNSwitch, portName 
OVNSwitchPort) error {
+       // Check if existing DNS record exists for switch port.
+       dnsUUID, err := o.nbctl("--format=csv", "--no-headings", "--data=bare", 
"--colum=_uuid", "find", "dns",
+               fmt.Sprintf("external_ids:lxd_switch_port=%s", 
string(portName)),
+       )
+       if err != nil {
+               return err
+       }
+
+       dnsUUID = strings.TrimSpace(dnsUUID)
+       if dnsUUID != "" {
+               // Remove DNS record association from switch.
+               _, err = o.nbctl("remove", "logical_switch", 
string(switchName), "dns_records", dnsUUID)
+               if err != nil {
+                       return err
+               }
+
+               // Remove DNS record entry itself.
+               _, err = o.nbctl("destroy", "dns", dnsUUID)
+               if err != nil {
+                       return err
+               }
+       }
+
+       return nil
+}
+
 // LogicalSwitchPortDelete deletes a named logical switch port.
 func (o *OVN) LogicalSwitchPortDelete(portName OVNSwitchPort) error {
        _, err := o.nbctl("--if-exists", "lsp-del", string(portName))

From e6cb1ece6c6c8974dfceb7347235b659be4986a6 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 19 Aug 2020 12:41:26 +0100
Subject: [PATCH 5/8] lxd/network/openvswitch/ovn: Updates LogicalSwitchDelete
 to clear any remaining DNS records

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

diff --git a/lxd/network/openvswitch/ovn.go b/lxd/network/openvswitch/ovn.go
index aab9fb1980..e5bb67b062 100644
--- a/lxd/network/openvswitch/ovn.go
+++ b/lxd/network/openvswitch/ovn.go
@@ -268,7 +268,17 @@ func (o *OVN) LogicalSwitchDelete(switchName OVNSwitch) 
error {
                return err
        }
 
-       return o.logicalSwitchDHCPOptionsDelete(switchName)
+       err = o.logicalSwitchDHCPOptionsDelete(switchName)
+       if err != nil {
+               return err
+       }
+
+       err = o.logicalSwitchDNSRecordsDelete(switchName)
+       if err != nil {
+               return err
+       }
+
+       return nil
 }
 
 // LogicalSwitchSetIPAllocation sets the IP allocation config on the logical 
switch.
@@ -486,6 +496,28 @@ func (o *OVN) logicalSwitchDHCPOptionsDelete(switchName 
OVNSwitch) error {
        return nil
 }
 
+// logicalSwitchDNSRecordsDelete deletes any DNS records defined for a switch.
+func (o *OVN) logicalSwitchDNSRecordsDelete(switchName OVNSwitch) error {
+       existingOpts, err := o.nbctl("--format=csv", "--no-headings", 
"--data=bare", "--colum=_uuid", "find", "dns",
+               fmt.Sprintf("external_ids:lxd_switch=%s", string(switchName)),
+       )
+       if err != nil {
+               return err
+       }
+
+       existingOpts = strings.TrimSpace(existingOpts)
+       if existingOpts != "" {
+               for _, uuid := range strings.Split(existingOpts, "\n") {
+                       _, err = o.nbctl("destroy", "dns", uuid)
+                       if err != nil {
+                               return err
+                       }
+               }
+       }
+
+       return nil
+}
+
 // LogicalSwitchPortAdd adds a named logical switch port to a logical switch.
 // If mayExist is true, then an existing resource of the same name is not 
treated as an error.
 func (o *OVN) LogicalSwitchPortAdd(switchName OVNSwitch, portName 
OVNSwitchPort, mayExist bool) error {

From a4c2ef37b148f91496f48c0d7fca669801f45b44 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 19 Aug 2020 12:41:54 +0100
Subject: [PATCH 6/8] lxd/network/network/utils/ovn: Updates
 OVNInstanceDevicePortAdd to take instanceName for DNS records

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

diff --git a/lxd/network/network_utils_ovn.go b/lxd/network/network_utils_ovn.go
index 03123de306..34ad39b59e 100644
--- a/lxd/network/network_utils_ovn.go
+++ b/lxd/network/network_utils_ovn.go
@@ -9,14 +9,14 @@ import (
 
 // 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, deviceName 
string, mac net.HardwareAddr, ips []net.IP) (openvswitch.OVNSwitchPort, error) {
+func OVNInstanceDevicePortAdd(network Network, instanceID int, instanceName 
string, deviceName string, mac net.HardwareAddr, ips []net.IP) 
(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, deviceName, mac, ips)
+       return n.instanceDevicePortAdd(instanceID, instanceName, deviceName, 
mac, ips)
 }
 
 // OVNInstanceDevicePortDelete deletes a logical port from the OVN network's 
internal switch.

From aa22aa7c6ebc90e51423db60eaf5783f364d5bea Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 19 Aug 2020 12:42:22 +0100
Subject: [PATCH 7/8] lxd/network/driver/ovn: Updates instance port functions
 to setup and remove DNS records

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

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index 2beb096fd9..1c6d914674 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -1217,7 +1217,7 @@ func (n *ovn) getInstanceDevicePortName(instanceID int, 
deviceName string) openv
 }
 
 // instanceDevicePortAdd adds an instance device port to the internal logical 
switch and returns the port name.
-func (n *ovn) instanceDevicePortAdd(instanceID int, deviceName string, mac 
net.HardwareAddr, ips []net.IP) (openvswitch.OVNSwitchPort, error) {
+func (n *ovn) instanceDevicePortAdd(instanceID int, instanceName string, 
deviceName string, mac net.HardwareAddr, ips []net.IP) 
(openvswitch.OVNSwitchPort, error) {
        var dhcpV4ID, dhcpv6ID string
 
        revert := revert.New()
@@ -1276,6 +1276,11 @@ func (n *ovn) instanceDevicePortAdd(instanceID int, 
deviceName string, mac net.H
                return "", err
        }
 
+       err = client.LogicalSwitchPortSetDNS(n.getIntSwitchName(), 
instancePortName, fmt.Sprintf("%s.%s", instanceName, n.getDomainName()))
+       if err != nil {
+               return "", err
+       }
+
        revert.Success()
        return instancePortName, nil
 }
@@ -1294,5 +1299,10 @@ func (n *ovn) instanceDevicePortDelete(instanceID int, 
deviceName string) error
                return err
        }
 
+       err = client.LogicalSwitchPortDeleteDNS(n.getIntSwitchName(), 
instancePortName)
+       if err != nil {
+               return err
+       }
+
        return nil
 }

From 9e0702680bc66af2312070350615363c22121278 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 19 Aug 2020 12:42:49 +0100
Subject: [PATCH 8/8] lxd/device/nic/ovn: Updates usage of
 network.OVNInstanceDevicePortAdd to supply instance name for DNS records

Signed-off-by: Thomas Parrott <thomas.parr...@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 5631b02a97..4b6db95bcd 100644
--- a/lxd/device/nic_ovn.go
+++ b/lxd/device/nic_ovn.go
@@ -233,7 +233,7 @@ 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.name, mac, ips)
+       logicalPortName, err := network.OVNInstanceDevicePortAdd(d.network, 
d.inst.ID(), d.inst.Name(), d.name, mac, ips)
        if err != nil {
                return nil, err
        }
_______________________________________________
lxc-devel mailing list
lxc-devel@lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to