diff options
Diffstat (limited to 'xenserver/opt_xensource_libexec_InterfaceReconfigureVswitch.py')
| -rw-r--r-- | xenserver/opt_xensource_libexec_InterfaceReconfigureVswitch.py | 447 |
1 files changed, 447 insertions, 0 deletions
diff --git a/xenserver/opt_xensource_libexec_InterfaceReconfigureVswitch.py b/xenserver/opt_xensource_libexec_InterfaceReconfigureVswitch.py new file mode 100644 index 000000000..c08385918 --- /dev/null +++ b/xenserver/opt_xensource_libexec_InterfaceReconfigureVswitch.py @@ -0,0 +1,447 @@ +# Copyright (c) 2008,2009 Citrix Systems, Inc. +# Copyright (c) 2009 Nicira Networks. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation; version 2.1 only. with the special +# exception on linking described in file LICENSE. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +from InterfaceReconfigure import * + +# +# Bare Network Devices -- network devices without IP configuration +# + +def netdev_down(netdev): + """Bring down a bare network device""" + if not netdev_exists(netdev): + log("netdev: down: device %s does not exist, ignoring" % netdev) + return + run_command(["/sbin/ifconfig", netdev, 'down']) + +def netdev_up(netdev, mtu=None): + """Bring up a bare network device""" + if not netdev_exists(netdev): + raise Error("netdev: up: device %s does not exist" % netdev) + + if mtu: + mtu = ["mtu", mtu] + else: + mtu = [] + + run_command(["/sbin/ifconfig", netdev, 'up'] + mtu) + +# +# Bridges +# + +def pif_bridge_name(pif): + """Return the bridge name of a pif. + + PIF must not be a VLAN and must be a bridged PIF.""" + + pifrec = db().get_pif_record(pif) + + if pif_is_vlan(pif): + raise Error("PIF %(uuid)s cannot be a bridge, VLAN is %(VLAN)s" % pifrec) + + nwrec = db().get_network_record(pifrec['network']) + + if nwrec['bridge']: + return nwrec['bridge'] + else: + raise Error("PIF %(uuid)s does not have a bridge name" % pifrec) + +# +# PIF miscellanea +# + +def pif_currently_in_use(pif): + """Determine if a PIF is currently in use. + + A PIF is determined to be currently in use if + - PIF.currently-attached is true + - Any bond master is currently attached + - Any VLAN master is currently attached + """ + rec = db().get_pif_record(pif) + if rec['currently_attached']: + log("configure_datapath: %s is currently attached" % (pif_netdev_name(pif))) + return True + for b in pif_get_bond_masters(pif): + if pif_currently_in_use(b): + log("configure_datapath: %s is in use by BOND master %s" % (pif_netdev_name(pif),pif_netdev_name(b))) + return True + for v in pif_get_vlan_masters(pif): + if pif_currently_in_use(v): + log("configure_datapath: %s is in use by VLAN master %s" % (pif_netdev_name(pif),pif_netdev_name(v))) + return True + return False + +# +# Datapath Configuration +# + +def pif_datapath(pif): + """Return the datapath PIF associated with PIF. +For a non-VLAN PIF, the datapath name is the bridge name. +For a VLAN PIF, the datapath name is the bridge name for the PIF's VLAN slave. +""" + if pif_is_vlan(pif): + return pif_datapath(pif_get_vlan_slave(pif)) + + pifrec = db().get_pif_record(pif) + nwrec = db().get_network_record(pifrec['network']) + if not nwrec['bridge']: + return None + else: + return pif + +def datapath_get_physical_pifs(pif): + """Return the PIFs for the physical network device(s) associated with a datapath PIF. +For a bond master PIF, these are the bond slave PIFs. +For a non-VLAN, non-bond master PIF, the PIF is its own physical device PIF. + +A VLAN PIF cannot be a datapath PIF. +""" + if pif_is_vlan(pif): + # Seems like overkill... + raise Error("get-physical-pifs should not get passed a VLAN") + elif pif_is_bond(pif): + return pif_get_bond_slaves(pif) + else: + return [pif] + +def datapath_deconfigure_physical(netdev): + return ['--', '--if-exists', 'del-port', netdev] + +def datapath_configure_bond(pif,slaves): + bridge = pif_bridge_name(pif) + pifrec = db.get_pif_record(pif) + interface = pif_netdev_name(pif) + + argv = ['--', '--fake-iface', 'add-bond', bridge, interface] + for slave in slaves: + argv += [pif_netdev_name(slave)] + + # XXX need ovs-vsctl support + #if pifrec['MAC'] != "": + # argv += ['--add=port.%s.mac=%s' % (interface, pifrec['MAC'])] + + # Bonding options. + bond_options = { + "mode": "balance-slb", + "miimon": "100", + "downdelay": "200", + "updelay": "31000", + "use_carrier": "1", + } + # override defaults with values from other-config whose keys + # being with "bond-" + oc = pifrec['other_config'] + overrides = filter(lambda (key,val): + key.startswith("bond-"), oc.items()) + overrides = map(lambda (key,val): (key[5:], val), overrides) + bond_options.update(overrides) + for (name,val) in bond_options.items(): + # XXX need ovs-vsctl support for bond options + #argv += ["--add=bonding.%s.%s=%s" % (interface, name, val)] + pass + return argv + +def datapath_deconfigure_bond(netdev): + return ['--', '--if-exists', 'del-port', netdev] + +def datapath_deconfigure_ipdev(interface): + return ['--', '--if-exists', 'del-port', interface] + +def datapath_modify_config(commands): + if debug_mode(): + log("modifying configuration:") + for c in commands: + log(" %s" % c) + + rc = run_command(['/usr/bin/ovs-vsctl'] + ['--timeout=20'] + + [c for c in commands if not c.startswith('#')]) + if not rc: + raise Error("Failed to modify vswitch configuration") + return True + +# +# Toplevel Datapath Configuration. +# + +def configure_datapath(pif): + """Bring up the datapath configuration for PIF. + + Should be careful not to glitch existing users of the datapath, e.g. other VLANs etc. + + Should take care of tearing down other PIFs which encompass common physical devices. + + Returns a tuple containing + - A list containing the necessary vsctl command line arguments + - A list of additional devices which should be brought up after + the configuration is applied. + """ + + vsctl_argv = [] + extra_up_ports = [] + + bridge = pif_bridge_name(pif) + + physical_devices = datapath_get_physical_pifs(pif) + + # Determine additional devices to deconfigure. + # + # Given all physical devices which are part of this PIF we need to + # consider: + # - any additional bond which a physical device is part of. + # - any additional physical devices which are part of an additional bond. + # + # Any of these which are not currently in use should be brought + # down and deconfigured. + extra_down_bonds = [] + extra_down_ports = [] + for p in physical_devices: + for bond in pif_get_bond_masters(p): + if bond == pif: + log("configure_datapath: leaving bond %s up" % pif_netdev_name(bond)) + continue + if bond in extra_down_bonds: + continue + if db().get_pif_record(bond)['currently_attached']: + log("configure_datapath: implicitly tearing down currently-attached bond %s" % pif_netdev_name(bond)) + + extra_down_bonds += [bond] + + for s in pif_get_bond_slaves(bond): + if s in physical_devices: + continue + if s in extra_down_ports: + continue + if pif_currently_in_use(s): + continue + extra_down_ports += [s] + + log("configure_datapath: bridge - %s" % bridge) + log("configure_datapath: physical - %s" % [pif_netdev_name(p) for p in physical_devices]) + log("configure_datapath: extra ports - %s" % [pif_netdev_name(p) for p in extra_down_ports]) + log("configure_datapath: extra bonds - %s" % [pif_netdev_name(p) for p in extra_down_bonds]) + + # Need to fully deconfigure any bridge which any of the: + # - physical devices + # - bond devices + # - sibling devices + # refers to + for brpif in physical_devices + extra_down_ports + extra_down_bonds: + if brpif == pif: + continue + b = pif_bridge_name(brpif) + #ifdown(b) + # XXX + netdev_down(b) + vsctl_argv += ['# remove bridge %s' % b] + vsctl_argv += ['--', '--if-exists', 'del-br', b] + + for n in extra_down_ports: + dev = pif_netdev_name(n) + vsctl_argv += ['# deconfigure sibling physical device %s' % dev] + vsctl_argv += datapath_deconfigure_physical(dev) + netdev_down(dev) + + for n in extra_down_bonds: + dev = pif_netdev_name(n) + vsctl_argv += ['# deconfigure bond device %s' % dev] + vsctl_argv += datapath_deconfigure_bond(dev) + netdev_down(dev) + + for p in physical_devices: + dev = pif_netdev_name(p) + vsctl_argv += ['# deconfigure physical port %s' % dev] + vsctl_argv += datapath_deconfigure_physical(dev) + + if len(physical_devices) > 1: + vsctl_argv += ['# deconfigure bond %s' % pif_netdev_name(pif)] + vsctl_argv += datapath_deconfigure_bond(pif_netdev_name(pif)) + vsctl_argv += ['# configure bond %s' % pif_netdev_name(pif)] + vsctl_argv += datapath_configure_bond(pif, physical_devices) + extra_up_ports += [pif_netdev_name(pif)] + else: + iface = pif_netdev_name(physical_devices[0]) + vsctl_argv += ['# add physical device %s' % iface] + vsctl_argv += ['--', 'add-port', bridge, iface] + + return vsctl_argv,extra_up_ports + +def deconfigure_datapath(pif): + vsctl_argv = [] + + bridge = pif_bridge_name(pif) + + physical_devices = datapath_get_physical_pifs(pif) + + log("deconfigure_datapath: bridge - %s" % bridge) + log("deconfigure_datapath: physical devices - %s" % [pif_netdev_name(p) for p in physical_devices]) + + for p in physical_devices: + dev = pif_netdev_name(p) + vsctl_argv += ['# deconfigure physical port %s' % dev] + vsctl_argv += datapath_deconfigure_physical(dev) + netdev_down(dev) + + if len(physical_devices) > 1: + vsctl_argv += ['# deconfigure bond %s' % pif_netdev_name(pif)] + vsctl_argv += datapath_deconfigure_bond(pif_netdev_name(pif)) + + vsctl_argv += ['# deconfigure bridge %s' % bridge] + vsctl_argv += ['--', '--if-exists', 'del-br', bridge] + + return vsctl_argv + +# +# +# + +class DatapathVswitch(Datapath): + def __init__(self, pif): + Datapath.__init__(self, pif) + self._dp = pif_datapath(pif) + self._ipdev = pif_ipdev_name(pif) + + if pif_is_vlan(pif) and not self._dp: + raise Error("Unbridged VLAN devices not implemented yet") + + log("Configured for Vswitch datapath") + + def configure_ipdev(self, cfg): + cfg.write("TYPE=Ethernet\n") + + def preconfigure(self, parent): + vsctl_argv = [] + extra_ports = [] + + pifrec = db().get_pif_record(self._pif) + + ipdev = self._ipdev + bridge = pif_bridge_name(self._dp) + c,e = configure_datapath(self._dp) + vsctl_argv += c + extra_ports += e + + if pif_is_vlan(pif): + datapath = pif_datapath(pif) + vsctl_argv += ['--', 'add-br', bridge, datapath, pifrec['VLAN']] + else: + vsctl_argv += ['--', 'add-br', bridge] + + xs_network_uuids = [] + for nwpif in db().get_pifs_by_device(db().get_pif_record(self._pif)['device']): + rec = db().get_pif_record(nwpif) + + # When state is read from dbcache PIF.currently_attached + # is always assumed to be false... Err on the side of + # listing even detached networks for the time being. + #if nwpif != pif and not rec['currently_attached']: + # log("Network PIF %s not currently attached (%s)" % (rec['uuid'],pifrec['uuid'])) + # continue + nwrec = db().get_network_record(rec['network']) + xs_network_uuids += [nwrec['uuid']] + + vsctl_argv += ['# configure xs-network-uuids'] + vsctl_argv += ['--', 'br-set-external-id', bridge, + 'xs-network-uuids', ';'.join(xs_network_uuids)] + + vsctl_argv += ["# deconfigure ipdev %s" % ipdev] + vsctl_argv += datapath_deconfigure_ipdev(ipdev) + vsctl_argv += ["# reconfigure ipdev %s" % ipdev] + vsctl_argv += ['--', 'add-port', bridge, ipdev] + + # XXX Needs support in ovs-vsctl + #if bridge == ipdev: + # vsctl_argv += ['--add=bridge.%s.mac=%s' % (bridge, pifrec['MAC'])] + #else: + # vsctl_argv += ['--add=iface.%s.mac=%s' % (ipdev, pifrec['MAC'])] + + self._vsctl_argv = vsctl_argv + self._extra_ports = extra_ports + + def bring_down_existing(self): + pass + + def configure(self): + # Bring up physical devices. ovs-vswitchd initially enables or + # disables bond slaves based on whether carrier is detected + # when they are added, and a network device that is down + # always reports "no carrier". + physical_devices = datapath_get_physical_pifs(self._dp) + + for p in physical_devices: + oc = db().get_pif_record(p)['other_config'] + + dev = pif_netdev_name(p) + + mtu = mtu_setting(oc) + + netdev_up(dev, mtu) + + settings, offload = ethtool_settings(oc) + if len(settings): + run_command(['/sbin/ethtool', '-s', dev] + settings) + if len(offload): + run_command(['/sbin/ethtool', '-K', dev] + offload) + + datapath_modify_config(self._vsctl_argv) + + def post(self): + for p in self._extra_ports: + log("action_up: bring up %s" % p) + netdev_up(p) + + def bring_down(self): + vsctl_argv = [] + + dp = self._dp + ipdev = self._ipdev + + bridge = pif_bridge_name(dp) + + #nw = db().get_pif_record(self._pif)['network'] + #nwrec = db().get_network_record(nw) + #vsctl_argv += ['# deconfigure xs-network-uuids'] + #vsctl_argv += ['--del-entry=bridge.%s.xs-network-uuids=%s' % (bridge,nwrec['uuid'])] + + log("deconfigure ipdev %s on %s" % (ipdev,bridge)) + vsctl_argv += ["# deconfigure ipdev %s" % ipdev] + vsctl_argv += datapath_deconfigure_ipdev(ipdev) + + if pif_is_vlan(self._pif): + # If the VLAN's slave is attached, leave datapath setup. + slave = pif_get_vlan_slave(self._pif) + if db().get_pif_record(slave)['currently_attached']: + log("action_down: vlan slave is currently attached") + dp = None + + # If the VLAN's slave has other VLANs that are attached, leave datapath setup. + for master in pif_get_vlan_masters(slave): + if master != self._pif and db().get_pif_record(master)['currently_attached']: + log("action_down: vlan slave has other master: %s" % pif_netdev_name(master)) + dp = None + + # Otherwise, take down the datapath too (fall through) + if dp: + log("action_down: no more masters, bring down slave %s" % bridge) + else: + # Stop here if this PIF has attached VLAN masters. + masters = [db().get_pif_record(m)['VLAN'] for m in pif_get_vlan_masters(self._pif) if db().get_pif_record(m)['currently_attached']] + if len(masters) > 0: + log("Leaving datapath %s up due to currently attached VLAN masters %s" % (bridge, masters)) + dp = None + + if dp: + vsctl_argv += deconfigure_datapath(dp) + datapath_modify_config(vsctl_argv) |
