The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/8262
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) === Supports either native or OVS bridges. And then uses the existing connection functions used for managed bridge uplinks to connect OVN router to uplink. Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
From 12ae61c323fd04f2b18a9076c88e2ca545484d93 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 16 Dec 2020 12:12:41 +0000 Subject: [PATCH] lxd/network/driver/ovn: Adds support for physical uplink interface to be a bridge Either native or OVS. And then uses the existing connection functions used for managed bridge uplinks to connect OVN router to uplink. Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/network/driver_ovn.go | 313 +++++++++++++++++++++++--------------- 1 file changed, 188 insertions(+), 125 deletions(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index 8b17895a8c..3e68e6018d 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -878,88 +878,118 @@ func (n *ovn) uplinkPortBridgeVars(uplinkNet Network) *ovnUplinkPortBridgeVars { // startUplinkPortBridge creates veth pair (if doesn't exist), creates OVS bridge (if doesn't exist) and // connects veth pair to uplink bridge and OVS bridge. func (n *ovn) startUplinkPortBridge(uplinkNet Network) error { + if uplinkNet.Config()["bridge.driver"] != "openvswitch" { + return n.startUplinkPortBridgeNative(uplinkNet, uplinkNet.Name()) + } + + return n.startUplinkPortBridgeOVS(uplinkNet, uplinkNet.Name()) +} + +// startUplinkPortBridgeNative connects an OVN logical router to an uplink native bridge. +func (n *ovn) startUplinkPortBridgeNative(uplinkNet Network, bridgeDevice string) error { // Do this after gaining lock so that on failure we revert before release locking. revert := revert.New() defer revert.Fail() - ovs := openvswitch.NewOVS() - // If uplink is a native bridge, then use a separate OVS bridge with veth pair connection to native bridge. - if uplinkNet.Config()["bridge.driver"] != "openvswitch" { - vars := n.uplinkPortBridgeVars(uplinkNet) - - // Create veth pair if needed. - if !InterfaceExists(vars.uplinkEnd) && !InterfaceExists(vars.ovsEnd) { - _, err := shared.RunCommand("ip", "link", "add", "dev", vars.uplinkEnd, "type", "veth", "peer", "name", vars.ovsEnd) - if err != nil { - return errors.Wrapf(err, "Failed to create the uplink veth interfaces %q and %q", vars.uplinkEnd, vars.ovsEnd) - } + vars := n.uplinkPortBridgeVars(uplinkNet) - revert.Add(func() { shared.RunCommand("ip", "link", "delete", vars.uplinkEnd) }) + // Create veth pair if needed. + if !InterfaceExists(vars.uplinkEnd) && !InterfaceExists(vars.ovsEnd) { + _, err := shared.RunCommand("ip", "link", "add", "dev", vars.uplinkEnd, "type", "veth", "peer", "name", vars.ovsEnd) + if err != nil { + return errors.Wrapf(err, "Failed to create the uplink veth interfaces %q and %q", vars.uplinkEnd, vars.ovsEnd) } - // Ensure that the veth interfaces inherit the uplink bridge's MTU (which the OVS bridge also inherits). - uplinkNetConfig := uplinkNet.Config() - if uplinkNetConfig["bridge.mtu"] != "" { - err := InterfaceSetMTU(vars.uplinkEnd, uplinkNetConfig["bridge.mtu"]) - if err != nil { - return err - } - - err = InterfaceSetMTU(vars.ovsEnd, uplinkNetConfig["bridge.mtu"]) - if err != nil { - return err - } - } + revert.Add(func() { shared.RunCommand("ip", "link", "delete", vars.uplinkEnd) }) + } - // Ensure correct sysctls are set on uplink veth interfaces to avoid getting IPv6 link-local addresses. - err := util.SysctlSet( - fmt.Sprintf("net/ipv6/conf/%s/disable_ipv6", vars.uplinkEnd), "1", - fmt.Sprintf("net/ipv6/conf/%s/disable_ipv6", vars.ovsEnd), "1", - fmt.Sprintf("net/ipv6/conf/%s/forwarding", vars.uplinkEnd), "0", - fmt.Sprintf("net/ipv6/conf/%s/forwarding", vars.ovsEnd), "0", - ) + // Ensure that the veth interfaces inherit the uplink bridge's MTU (which the OVS bridge also inherits). + uplinkNetConfig := uplinkNet.Config() + if uplinkNetConfig["bridge.mtu"] != "" { + err := InterfaceSetMTU(vars.uplinkEnd, uplinkNetConfig["bridge.mtu"]) if err != nil { - return errors.Wrapf(err, "Failed to configure uplink veth interfaces %q and %q", vars.uplinkEnd, vars.ovsEnd) + return err } - // Connect uplink end of veth pair to uplink bridge and bring up. - _, err = shared.RunCommand("ip", "link", "set", "master", uplinkNet.Name(), "dev", vars.uplinkEnd, "up") + err = InterfaceSetMTU(vars.ovsEnd, uplinkNetConfig["bridge.mtu"]) if err != nil { - return errors.Wrapf(err, "Failed to connect uplink veth interface %q to uplink bridge %q", vars.uplinkEnd, uplinkNet.Name()) + return err } + } - // Ensure uplink OVS end veth interface is up. - _, err = shared.RunCommand("ip", "link", "set", "dev", vars.ovsEnd, "up") - if err != nil { - return errors.Wrapf(err, "Failed to bring up uplink veth interface %q", vars.ovsEnd) - } + // Ensure correct sysctls are set on uplink veth interfaces to avoid getting IPv6 link-local addresses. + err := util.SysctlSet( + fmt.Sprintf("net/ipv6/conf/%s/disable_ipv6", vars.uplinkEnd), "1", + fmt.Sprintf("net/ipv6/conf/%s/disable_ipv6", vars.ovsEnd), "1", + fmt.Sprintf("net/ipv6/conf/%s/forwarding", vars.uplinkEnd), "0", + fmt.Sprintf("net/ipv6/conf/%s/forwarding", vars.ovsEnd), "0", + ) + if err != nil { + return errors.Wrapf(err, "Failed to configure uplink veth interfaces %q and %q", vars.uplinkEnd, vars.ovsEnd) + } - // Create uplink OVS bridge if needed. - err = ovs.BridgeAdd(vars.ovsBridge, true) - if err != nil { - return errors.Wrapf(err, "Failed to create uplink OVS bridge %q", vars.ovsBridge) - } + // Connect uplink end of veth pair to uplink bridge and bring up. + _, err = shared.RunCommand("ip", "link", "set", "master", bridgeDevice, "dev", vars.uplinkEnd, "up") + if err != nil { + return errors.Wrapf(err, "Failed to connect uplink veth interface %q to uplink bridge %q", vars.uplinkEnd, bridgeDevice) + } - // Connect OVS end veth interface to OVS bridge. - err = ovs.BridgePortAdd(vars.ovsBridge, vars.ovsEnd, true) - if err != nil { - return errors.Wrapf(err, "Failed to connect uplink veth interface %q to uplink OVS bridge %q", vars.ovsEnd, vars.ovsBridge) - } + // Ensure uplink OVS end veth interface is up. + _, err = shared.RunCommand("ip", "link", "set", "dev", vars.ovsEnd, "up") + if err != nil { + return errors.Wrapf(err, "Failed to bring up uplink veth interface %q", vars.ovsEnd) + } - // Associate OVS bridge to logical OVN provider. - err = ovs.OVNBridgeMappingAdd(vars.ovsBridge, uplinkNet.Name()) - if err != nil { - return errors.Wrapf(err, "Failed to associate uplink OVS bridge %q to OVN provider %q", vars.ovsBridge, uplinkNet.Name()) - } - } else { - // If uplink is an openvswitch bridge, have OVN logical provider connect directly to it. - err := ovs.OVNBridgeMappingAdd(uplinkNet.Name(), uplinkNet.Name()) - if err != nil { - return errors.Wrapf(err, "Failed to associate uplink OVS bridge %q to OVN provider %q", uplinkNet.Name(), uplinkNet.Name()) - } + // Create uplink OVS bridge if needed. + ovs := openvswitch.NewOVS() + err = ovs.BridgeAdd(vars.ovsBridge, true) + if err != nil { + return errors.Wrapf(err, "Failed to create uplink OVS bridge %q", vars.ovsBridge) + } + + // Connect OVS end veth interface to OVS bridge. + err = ovs.BridgePortAdd(vars.ovsBridge, vars.ovsEnd, true) + if err != nil { + return errors.Wrapf(err, "Failed to connect uplink veth interface %q to uplink OVS bridge %q", vars.ovsEnd, vars.ovsBridge) + } + + // Associate OVS bridge to logical OVN provider. + err = ovs.OVNBridgeMappingAdd(vars.ovsBridge, uplinkNet.Name()) + if err != nil { + return errors.Wrapf(err, "Failed to associate uplink OVS bridge %q to OVN provider %q", vars.ovsBridge, uplinkNet.Name()) + } + + // Attempt to learn uplink MAC. + n.pingOVNRouterIPv6() + + revert.Success() + return nil +} + +// startUplinkPortBridgeOVS connects an OVN logical router to an uplink OVS bridge. +func (n *ovn) startUplinkPortBridgeOVS(uplinkNet Network, bridgeDevice string) error { + // Do this after gaining lock so that on failure we revert before release locking. + revert := revert.New() + defer revert.Fail() + + // If uplink is an openvswitch bridge, have OVN logical provider connect directly to it. + ovs := openvswitch.NewOVS() + err := ovs.OVNBridgeMappingAdd(bridgeDevice, uplinkNet.Name()) + if err != nil { + return errors.Wrapf(err, "Failed to associate uplink OVS bridge %q to OVN provider %q", bridgeDevice, uplinkNet.Name()) } + // Attempt to learn uplink MAC. + n.pingOVNRouterIPv6() + + revert.Success() + return nil +} + +// pingOVNRouterIPv6 pings the OVN router's external IPv6 address to attempt to trigger MAC learning on uplink. +// This is to work around a bug in some versions of OVN. +func (n *ovn) pingOVNRouterIPv6() { routerExtPortIPv6 := net.ParseIP(n.config[ovnVolatileUplinkIPv6]) if routerExtPortIPv6 != nil { // Now that the OVN router is connected to the uplink bridge, attempt to ping the OVN @@ -986,15 +1016,10 @@ func (n *ovn) startUplinkPortBridge(uplinkNet Network) error { n.logger.Debug("OVN router external IPv6 address unreachable", log.Ctx{"ip": routerExtPortIPv6.String()}) }() } - - revert.Success() - return nil } // startUplinkPortPhysical creates OVS bridge (if doesn't exist) and connects uplink interface to the OVS bridge. func (n *ovn) startUplinkPortPhysical(uplinkNet Network) error { - vars := n.uplinkPortBridgeVars(uplinkNet) - // Do this after gaining lock so that on failure we revert before release locking. revert := revert.New() defer revert.Fail() @@ -1003,9 +1028,24 @@ func (n *ovn) startUplinkPortPhysical(uplinkNet Network) error { uplinkHostName := GetHostDevice(uplinkConfig["parent"], uplinkConfig["vlan"]) if !InterfaceExists(uplinkHostName) { - return fmt.Errorf("Uplink network %q is not started", uplinkNet.Name()) + return fmt.Errorf("Uplink network %q is not started (interface %q is missing)", uplinkNet.Name(), uplinkHostName) + } + + // Detect if uplink interface is a native bridge. + if IsNativeBridge(uplinkHostName) { + return n.startUplinkPortBridgeNative(uplinkNet, uplinkHostName) + } + + // Detect if uplink interface is a OVS bridge. + ovs := openvswitch.NewOVS() + isOVSBridge, _ := ovs.BridgeExists(uplinkHostName) + if isOVSBridge { + return n.startUplinkPortBridgeOVS(uplinkNet, uplinkHostName) } + // If uplink is a normal physical interface, then use a separate OVS bridge and connect uplink to it. + vars := n.uplinkPortBridgeVars(uplinkNet) + // Ensure correct sysctls are set on uplink interface to avoid getting IPv6 link-local addresses. err := util.SysctlSet( fmt.Sprintf("net/ipv6/conf/%s/disable_ipv6", uplinkHostName), "1", @@ -1016,7 +1056,6 @@ func (n *ovn) startUplinkPortPhysical(uplinkNet Network) error { } // Create uplink OVS bridge if needed. - ovs := openvswitch.NewOVS() err = ovs.BridgeAdd(vars.ovsBridge, true) if err != nil { return errors.Wrapf(err, "Failed to create uplink OVS bridge %q", vars.ovsBridge) @@ -1100,66 +1139,58 @@ func (n *ovn) deleteUplinkPort() error { return nil } -// deleteUplinkPortBridge deletes uplink OVS bridge, OVN bridge mappings and veth interfaces if not in use. +// deleteUplinkPortBridge disconnects the uplink port from the bridge and performs any cleanup. func (n *ovn) deleteUplinkPortBridge(uplinkNet Network) error { - // If uplink is a native bridge, then clean up separate OVS bridge and veth pair connection if not in use. if uplinkNet.Config()["bridge.driver"] != "openvswitch" { - // Check OVS uplink bridge exists, if it does, check whether the uplink network is in use. - removeVeths := false - vars := n.uplinkPortBridgeVars(uplinkNet) - if InterfaceExists(vars.ovsBridge) { - uplinkUsed, err := n.checkUplinkUse() - if err != nil { - return err - } - - // Remove OVS bridge if the uplink network isn't used by any other OVN networks. - if !uplinkUsed { - removeVeths = true + return n.deleteUplinkPortBridgeNative(uplinkNet) + } - ovs := openvswitch.NewOVS() - err = ovs.OVNBridgeMappingDelete(vars.ovsBridge, uplinkNet.Name()) - if err != nil { - return err - } + return n.deleteUplinkPortBridgeOVS(uplinkNet) +} - err = ovs.BridgeDelete(vars.ovsBridge) - if err != nil { - return err - } - } - } else { - removeVeths = true // Remove the veths if OVS bridge already gone. +// deleteUplinkPortBridge deletes uplink OVS bridge, OVN bridge mappings and veth interfaces if not in use. +func (n *ovn) deleteUplinkPortBridgeNative(uplinkNet Network) error { + // Check OVS uplink bridge exists, if it does, check whether the uplink network is in use. + removeVeths := false + vars := n.uplinkPortBridgeVars(uplinkNet) + if InterfaceExists(vars.ovsBridge) { + uplinkUsed, err := n.checkUplinkUse() + if err != nil { + return err } - // Remove the veth interfaces if they exist. - if removeVeths { - if InterfaceExists(vars.uplinkEnd) { - _, err := shared.RunCommand("ip", "link", "delete", "dev", vars.uplinkEnd) - if err != nil { - return errors.Wrapf(err, "Failed to delete the uplink veth interface %q", vars.uplinkEnd) - } + // Remove OVS bridge if the uplink network isn't used by any other OVN networks. + if !uplinkUsed { + removeVeths = true + + ovs := openvswitch.NewOVS() + err = ovs.OVNBridgeMappingDelete(vars.ovsBridge, uplinkNet.Name()) + if err != nil { + return err } - if InterfaceExists(vars.ovsEnd) { - _, err := shared.RunCommand("ip", "link", "delete", "dev", vars.ovsEnd) - if err != nil { - return errors.Wrapf(err, "Failed to delete the uplink veth interface %q", vars.ovsEnd) - } + err = ovs.BridgeDelete(vars.ovsBridge) + if err != nil { + return err } } } else { - uplinkUsed, err := n.checkUplinkUse() - if err != nil { - return err + removeVeths = true // Remove the veths if OVS bridge already gone. + } + + // Remove the veth interfaces if they exist. + if removeVeths { + if InterfaceExists(vars.uplinkEnd) { + _, err := shared.RunCommand("ip", "link", "delete", "dev", vars.uplinkEnd) + if err != nil { + return errors.Wrapf(err, "Failed to delete the uplink veth interface %q", vars.uplinkEnd) + } } - // Remove uplink OVS bridge mapping if not in use by other OVN networks. - if !uplinkUsed { - ovs := openvswitch.NewOVS() - err = ovs.OVNBridgeMappingDelete(uplinkNet.Name(), uplinkNet.Name()) + if InterfaceExists(vars.ovsEnd) { + _, err := shared.RunCommand("ip", "link", "delete", "dev", vars.ovsEnd) if err != nil { - return err + return errors.Wrapf(err, "Failed to delete the uplink veth interface %q", vars.ovsEnd) } } } @@ -1167,8 +1198,44 @@ func (n *ovn) deleteUplinkPortBridge(uplinkNet Network) error { return nil } +// deleteUplinkPortBridge deletes OVN bridge mappings if not in use. +func (n *ovn) deleteUplinkPortBridgeOVS(uplinkNet Network) error { + uplinkUsed, err := n.checkUplinkUse() + if err != nil { + return err + } + + // Remove uplink OVS bridge mapping if not in use by other OVN networks. + if !uplinkUsed { + ovs := openvswitch.NewOVS() + err = ovs.OVNBridgeMappingDelete(uplinkNet.Name(), uplinkNet.Name()) + if err != nil { + return err + } + } + + return nil +} + // deleteUplinkPortPhysical deletes uplink OVS bridge and OVN bridge mappings if not in use. func (n *ovn) deleteUplinkPortPhysical(uplinkNet Network) error { + uplinkConfig := uplinkNet.Config() + uplinkHostName := GetHostDevice(uplinkConfig["parent"], uplinkConfig["vlan"]) + + // Detect if uplink interface is a native bridge. + if IsNativeBridge(uplinkHostName) { + return n.deleteUplinkPortBridgeNative(uplinkNet) + } + + // Detect if uplink interface is a OVS bridge. + ovs := openvswitch.NewOVS() + isOVSBridge, _ := ovs.BridgeExists(uplinkHostName) + if isOVSBridge { + return n.deleteUplinkPortBridgeOVS(uplinkNet) + } + + // Otherwise if uplink is normal physical interface, attempt cleanup of OVS bridge. + // Check OVS uplink bridge exists, if it does, check whether the uplink network is in use. releaseIF := false vars := n.uplinkPortBridgeVars(uplinkNet) @@ -1194,18 +1261,14 @@ func (n *ovn) deleteUplinkPortPhysical(uplinkNet Network) error { } } } else { - releaseIF = true // Remove the veths if OVS bridge already gone. + releaseIF = true // Bring uplink interface down if not needed. } - // Bring down uplink interface if exists. - if releaseIF { - uplinkConfig := uplinkNet.Config() - uplinkDev := GetHostDevice(uplinkConfig["parent"], uplinkConfig["vlan"]) - if InterfaceExists(uplinkDev) { - _, err := shared.RunCommand("ip", "link", "set", uplinkDev, "down") - if err != nil { - return errors.Wrapf(err, "Failed to bring down uplink interface %q", uplinkDev) - } + // Bring down uplink interface if not used and exists. + if releaseIF && InterfaceExists(uplinkHostName) { + _, err := shared.RunCommand("ip", "link", "set", uplinkHostName, "down") + if err != nil { + return errors.Wrapf(err, "Failed to bring down uplink interface %q", uplinkHostName) } }
_______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel