[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-API] [PATCH 30 of 33] interface-reconfigure: move datapath configuration to module
Provide a base class and move Bridge specific code into a specific subclass. Signed-off-by: Ian Campbell <ian.campbell@xxxxxxxxxx> diff -r 28a62b543c4c -r 0e07bf7f3fe1 scripts/InterfaceReconfigure.py --- a/scripts/InterfaceReconfigure.py Fri Dec 18 14:16:32 2009 +0000 +++ b/scripts/InterfaceReconfigure.py Fri Dec 18 14:16:32 2009 +0000 @@ -1,4 +1,5 @@ import syslog +import os from xml.dom.minidom import getDOMImplementation from xml.dom.minidom import parse as parseXML @@ -18,6 +19,145 @@ def __init__(self, msg): Exception.__init__(self) self.msg = msg + +# +# Run external utilities +# + +def run_command(command): + log("Running command: " + ' '.join(command)) + rc = os.spawnl(os.P_WAIT, command[0], *command) + if rc != 0: + log("Command failed %d: " % rc + ' '.join(command)) + return False + return True + +# +# Configuration File Handling. +# + +class ConfigurationFile(object): + """Write a file, tracking old and new versions. + + Supports writing a new version of a file and applying and + reverting those changes. + """ + + __STATE = {"OPEN":"OPEN", + "NOT-APPLIED":"NOT-APPLIED", "APPLIED":"APPLIED", + "REVERTED":"REVERTED", "COMMITTED": "COMMITTED"} + + def __init__(self, path): + dirname,basename = os.path.split(path) + + self.__state = self.__STATE['OPEN'] + self.__children = [] + + self.__path = os.path.join(dirname, basename) + self.__oldpath = os.path.join(dirname, "." + basename + ".xapi-old") + self.__newpath = os.path.join(dirname, "." + basename + ".xapi-new") + + self.__f = open(self.__newpath, "w") + + def attach_child(self, child): + self.__children.append(child) + + def path(self): + return self.__path + + def readlines(self): + try: + return open(self.path()).readlines() + except: + return "" + + def write(self, args): + if self.__state != self.__STATE['OPEN']: + raise Error("Attempt to write to file in state %s" % self.__state) + self.__f.write(args) + + def close(self): + if self.__state != self.__STATE['OPEN']: + raise Error("Attempt to close file in state %s" % self.__state) + + self.__f.close() + self.__state = self.__STATE['NOT-APPLIED'] + + def changed(self): + if self.__state != self.__STATE['NOT-APPLIED']: + raise Error("Attempt to compare file in state %s" % self.__state) + + return True + + def apply(self): + if self.__state != self.__STATE['NOT-APPLIED']: + raise Error("Attempt to apply configuration from state %s" % self.__state) + + for child in self.__children: + child.apply() + + log("Applying changes to %s configuration" % self.__path) + + # Remove previous backup. + if os.access(self.__oldpath, os.F_OK): + os.unlink(self.__oldpath) + + # Save current configuration. + if os.access(self.__path, os.F_OK): + os.link(self.__path, self.__oldpath) + os.unlink(self.__path) + + # Apply new configuration. + assert(os.path.exists(self.__newpath)) + os.link(self.__newpath, self.__path) + + # Remove temporary file. + os.unlink(self.__newpath) + + self.__state = self.__STATE['APPLIED'] + + def revert(self): + if self.__state != self.__STATE['APPLIED']: + raise Error("Attempt to revert configuration from state %s" % self.__state) + + for child in self.__children: + child.revert() + + log("Reverting changes to %s configuration" % self.__path) + + # Remove existing new configuration + if os.access(self.__newpath, os.F_OK): + os.unlink(self.__newpath) + + # Revert new configuration. + if os.access(self.__path, os.F_OK): + os.link(self.__path, self.__newpath) + os.unlink(self.__path) + + # Revert to old configuration. + if os.access(self.__oldpath, os.F_OK): + os.link(self.__oldpath, self.__path) + os.unlink(self.__oldpath) + + # Leave .*.xapi-new as an aid to debugging. + + self.__state = self.__STATE['REVERTED'] + + def commit(self): + if self.__state != self.__STATE['APPLIED']: + raise Error("Attempt to commit configuration from state %s" % self.__state) + + for child in self.__children: + child.commit() + + log("Committing changes to %s configuration" % self.__path) + + if os.access(self.__oldpath, os.F_OK): + os.unlink(self.__oldpath) + if os.access(self.__newpath, os.F_OK): + os.unlink(self.__newpath) + + self.__state = self.__STATE['COMMITTED'] # # Helper functions for encoding/decoding database attributes to/from XML. @@ -453,6 +593,38 @@ return None # +# IP Network Devices -- network devices with IP configuration +# +def pif_ipdev_name(pif): + """Return the ipdev name associated with pif""" + pifrec = db().get_pif_record(pif) + nwrec = db().get_network_record(pifrec['network']) + + if nwrec['bridge']: + # TODO: sanity check that nwrec['bridgeless'] != 'true' + return nwrec['bridge'] + else: + # TODO: sanity check that nwrec['bridgeless'] == 'true' + return pif_netdev_name(pif) + +# +# Bare Network Devices -- network devices without IP configuration +# + +def netdev_exists(netdev): + return os.path.exists("/sys/class/net/" + netdev) + +def pif_netdev_name(pif): + """Get the netdev name for a PIF.""" + + pifrec = db().get_pif_record(pif) + + if pif_is_vlan(pif): + return "%(device)s.%(VLAN)s" % pifrec + else: + return pifrec['device'] + +# # Bonded PIFs # def pif_is_bond(pif): @@ -529,3 +701,81 @@ vlans = [db().get_vlan_record(v) for v in pifrec['VLAN_slave_of']] return [v['untagged_PIF'] for v in vlans if v and db().pif_exists(v['untagged_PIF'])] +# +# Datapath base class +# + +class Datapath(object): + """Object encapsulating the actions necessary to (de)configure the + datapath for a given PIF. Does not include configuration of the + IP address on the ipdev. + """ + + def __init__(self, pif): + self._pif = pif + + def configure_ipdev(self, cfg): + """Write ifcfg TYPE field for an IPdev, plus any type specific + fields to cfg + """ + raise NotImplementedError + + def preconfigure(self, parent): + """Prepare datapath configuration for PIF, but do not actually + apply any changes. + + Any configuration files should be attached to parent. + """ + raise NotImplementedError + + def bring_down_existing(self): + """Tear down any existing network device configuration which + needs to be undone in order to bring this PIF up. + """ + raise NotImplementedError + + def configure(self): + """Apply the configuration prepared in the preconfigure stage. + + Should assume any configuration files changed attached in + the preconfigure stage are applied and bring up the + necesary devices to provide the datapath for the + PIF. + + Should not bring up the IPdev. + """ + raise NotImplementedError + + def post(self): + """Called after the IPdev has been brought up. + + Should do any final setup, including reinstating any + devices which were taken down in the bring_down_existing + hook. + """ + raise NotImplementedError + + def bring_down(self): + """Tear down and deconfigure the datapath. Should assume the + IPdev has already been brought down. + """ + raise NotImplementedError + +def DatapathFactory(pif): + # XXX Need a datapath object for bridgeless PIFs + + try: + network_conf = open("/etc/xensource/network.conf", 'r') + network_backend = network_conf.readline().strip() + network_conf.close() + except Exception, e: + raise Error("failed to determine network backend:" + e) + + if network_backend == "bridge": + from InterfaceReconfigureBridge import DatapathBridge + return DatapathBridge(pif) + elif network_backend == "vswitch": + from InterfaceReconfigureVswitch import DatapathVswitch + return DatapathVswitch(pif) + else: + raise Error("unknown network backend %s" % network_backend) diff -r 28a62b543c4c -r 0e07bf7f3fe1 scripts/InterfaceReconfigureBridge.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/InterfaceReconfigureBridge.py Fri Dec 18 14:16:32 2009 +0000 @@ -0,0 +1,485 @@ +from InterfaceReconfigure import * + +import sys +import time + +sysfs_bonding_masters = "/sys/class/net/bonding_masters" + +def open_pif_ifcfg(pif): + pifrec = db().get_pif_record(pif) + + interface = pif_netdev_name(pif) + log("Configuring %s (%s)" % (interface, pifrec['MAC'])) + + f = ConfigurationFile("/etc/sysconfig/network-scripts/ifcfg-%s" % interface) + + f.write("# DO NOT EDIT: This file (%s) was autogenerated by %s\n" % \ + (os.path.basename(f.path()), os.path.basename(sys.argv[0]))) + f.write("XEMANAGED=yes\n") + f.write("DEVICE=%s\n" % interface) + f.write("ONBOOT=no\n") + + return f + +# +# 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/ifdown", netdev]) + +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) + + run_command(["/sbin/ifup", netdev]) + +# +# Bonding driver +# + +def load_bonding_driver(): + log("Loading bonding driver") + run_command(["/sbin/modprobe", "bonding"]) + try: + # bond_device_exists() uses the contents of sysfs_bonding_masters to work out which devices + # have already been created. Unfortunately the driver creates "bond0" automatically at + # modprobe init. Get rid of this now or our accounting will go wrong. + f = open(sysfs_bonding_masters, "w") + f.write("-bond0") + f.close() + except IOError, e: + log("Failed to load bonding driver: %s" % e) + +def bonding_driver_loaded(): + lines = open("/proc/modules").read().split("\n") + modules = [line.split(" ")[0] for line in lines] + return "bonding" in modules + +def bond_device_exists(name): + f = open(sysfs_bonding_masters, "r") + bonds = f.readline().split() + f.close() + return name in bonds + +def __create_bond_device(name): + + if not bonding_driver_loaded(): + load_bonding_driver() + + if bond_device_exists(name): + log("bond master %s already exists, not creating" % name) + else: + log("Creating bond master %s" % name) + try: + f = open(sysfs_bonding_masters, "w") + f.write("+" + name) + f.close() + except IOError, e: + log("Failed to create %s: %s" % (name, e)) + +def create_bond_device(pif): + """Ensures that a bond master device exists in the kernel.""" + + if not pif_is_bond(pif): + return + + __create_bond_device(pif_netdev_name(pif)) + +def __destroy_bond_device(name): + if bond_device_exists(name): + retries = 10 # 10 * 0.5 seconds + while retries > 0: + retries = retries - 1 + log("Destroying bond master %s (%d attempts remain)" % (name,retries)) + try: + f = open(sysfs_bonding_masters, "w") + f.write("-" + name) + f.close() + retries = 0 + except IOError, e: + time.sleep(0.5) + else: + log("bond master %s does not exist, not destroying" % name) + +def destroy_bond_device(pif): + """No, Mr. Bond, I expect you to die.""" + + pifrec = db().get_pif_record(pif) + + if not pif_is_bond(pif): + return + + # If the bonding module isn't loaded then do nothing. + if not os.access(sysfs_bonding_masters, os.F_OK): + return + + name = pif_netdev_name(pif) + + __destroy_bond_device(name) + +# +# Bridges +# + +def pif_is_bridged(pif): + pifrec = db().get_pif_record(pif) + nwrec = db().get_network_record(pifrec['network']) + + if nwrec['bridge']: + # TODO: sanity check that nwrec['bridgeless'] != 'true' + return True + else: + # TODO: sanity check that nwrec['bridgeless'] == 'true' + return False + +def pif_bridge_name(pif): + """Return the bridge name of a pif. + + PIF must be a bridged PIF.""" + pifrec = db().get_pif_record(pif) + + 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) + +# +# Bring Interface up/down. +# + +def bring_down_interface(pif, destroy=False): + """Bring down the interface associated with PIF. + + Brings down the given interface as well as any physical interfaces + which are bond slaves of this one. This is because they will be + required when the bond is brought up.""" + + def destroy_bridge(pif): + """Bring down the bridge associated with a PIF.""" + #if not pif_is_bridged(pif): + # return + bridge = pif_bridge_name(pif) + if not netdev_exists(bridge): + log("destroy_bridge: bridge %s does not exist, ignoring" % bridge) + return + log("Destroy bridge %s" % bridge) + netdev_down(bridge) + run_command(["/usr/sbin/brctl", "delbr", bridge]) + + def destroy_vlan(pif): + vlan = pif_netdev_name(pif) + if not netdev_exists(vlan): + log("vconfig del: vlan %s does not exist, ignoring" % vlan) + return + log("Destroy vlan device %s" % vlan) + run_command(["/sbin/vconfig", "rem", vlan]) + + if pif_is_vlan(pif): + interface = pif_netdev_name(pif) + log("bring_down_interface: %s is a VLAN" % interface) + netdev_down(interface) + + if destroy: + destroy_vlan(pif) + destroy_bridge(pif) + else: + return + + slave = pif_get_vlan_slave(pif) + if db().get_pif_record(slave)['currently_attached']: + log("bring_down_interface: vlan slave is currently attached") + return + + masters = pif_get_vlan_masters(slave) + masters = [m for m in masters if m != pif and db().get_pif_record(m)['currently_attached']] + if len(masters) > 0: + log("bring_down_interface: vlan slave has other masters") + return + + log("bring_down_interface: no more masters, bring down vlan slave %s" % pif_netdev_name(slave)) + pif = slave + else: + vlan_masters = pif_get_vlan_masters(pif) + log("vlan masters of %s - %s" % (db().get_pif_record(pif)['device'], [pif_netdev_name(m) for m in vlan_masters])) + if len([m for m in vlan_masters if db().get_pif_record(m)['currently_attached']]) > 0: + log("Leaving %s up due to currently attached VLAN masters" % pif_netdev_name(pif)) + return + + # pif is now either a bond or a physical device which needs to be brought down + + # Need to bring down bond slaves first since the bond device + # must be up to enslave/unenslave. + bond_slaves = pif_get_bond_slaves_sorted(pif) + log("bond slaves of %s - %s" % (db().get_pif_record(pif)['device'], [pif_netdev_name(s) for s in bond_slaves])) + for slave in bond_slaves: + slave_interface = pif_netdev_name(slave) + if db().get_pif_record(slave)['currently_attached']: + log("leave bond slave %s up (currently attached)" % slave_interface) + continue + log("bring down bond slave %s" % slave_interface) + netdev_down(slave_interface) + # Also destroy the bridge associated with the slave, since + # it will carry the MAC address and possibly an IP address + # leading to confusion. + destroy_bridge(slave) + + interface = pif_netdev_name(pif) + log("Bring interface %s down" % interface) + netdev_down(interface) + + if destroy: + destroy_bond_device(pif) + destroy_bridge(pif) + +def interface_is_up(pif): + try: + interface = pif_netdev_name(pif) + state = open("/sys/class/net/%s/operstate" % interface).read().strip() + return state == "up" + except: + return False # interface prolly doesn't exist + +def bring_up_interface(pif): + """Bring up the interface associated with a PIF. + + Also bring up the interfaces listed in additional. + """ + + # VLAN on bond seems to need bond brought up explicitly, but VLAN + # on normal device does not. Might as well always bring it up. + if pif_is_vlan(pif): + slave = pif_get_vlan_slave(pif) + if not interface_is_up(slave): + bring_up_interface(slave) + + interface = pif_netdev_name(pif) + + create_bond_device(pif) + + log("Bring interface %s up" % interface) + netdev_up(interface) + + +# +# Datapath topology configuration. +# + +def _configure_physical_interface(pif): + """Write the configuration for a physical interface. + + Writes the configuration file for the physical interface described by + the pif object. + + Returns the open file handle for the interface configuration file. + """ + + pifrec = db().get_pif_record(pif) + + f = open_pif_ifcfg(pif) + + f.write("TYPE=Ethernet\n") + f.write("HWADDR=%(MAC)s\n" % pifrec) + + settings,offload = ethtool_settings(pifrec['other_config']) + if len(settings): + f.write("ETHTOOL_OPTS=\"%s\"\n" % str.join(" ", settings)) + if len(offload): + f.write("ETHTOOL_OFFLOAD_OPTS=\"%s\"\n" % str.join(" ", offload)) + + mtu = mtu_setting(pifrec['other_config']) + if mtu: + f.write("MTU=%s\n" % mtu) + + return f + +def pif_get_bond_slaves_sorted(pif): + pifrec = db().get_pif_record(pif) + + # build a list of slave's pifs + slave_pifs = pif_get_bond_slaves(pif) + + # Ensure any currently attached slaves are listed in the opposite order to the order in + # which they were attached. The first slave attached must be the last detached since + # the bond is using its MAC address. + try: + attached_slaves = open("/sys/class/net/%s/bonding/slaves" % pifrec['device']).readline().split() + for slave in attached_slaves: + pifs = [p for p in db().get_pifs_by_device(slave) if not pif_is_vlan(p)] + slave_pif = pifs[0] + slave_pifs.remove(slave_pif) + slave_pifs.insert(0, slave_pif) + except IOError: + pass + + return slave_pifs + +def _configure_bond_interface(pif): + """Write the configuration for a bond interface. + + Writes the configuration file for the bond interface described by + the pif object. Handles writing the configuration for the slave + interfaces. + + Returns the open file handle for the bond interface configuration + file. + """ + + pifrec = db().get_pif_record(pif) + + f = open_pif_ifcfg(pif) + + if pifrec['MAC'] != "": + f.write("MACADDR=%s\n" % pifrec['MAC']) + + for slave in pif_get_bond_slaves(pif): + s = _configure_physical_interface(slave) + s.write("MASTER=%(device)s\n" % pifrec) + s.write("SLAVE=yes\n") + s.close() + f.attach_child(s) + + settings,offload = ethtool_settings(pifrec['other_config']) + if len(settings): + f.write("ETHTOOL_OPTS=\"%s\"\n" % str.join(" ", settings)) + if len(offload): + f.write("ETHTOOL_OFFLOAD_OPTS=\"%s\"\n" % str.join(" ", offload)) + + mtu = mtu_setting(pifrec['other_config']) + if mtu: + f.write("MTU=%s\n" % mtu) + + # The bond option defaults + 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) + + # write the bond options to ifcfg-bondX + f.write('BONDING_OPTS="') + for (name,val) in bond_options.items(): + f.write("%s=%s " % (name,val)) + f.write('"\n') + return f + +def _configure_vlan_interface(pif): + """Write the configuration for a VLAN interface. + + Writes the configuration file for the VLAN interface described by + the pif object. Handles writing the configuration for the master + interface if necessary. + + Returns the open file handle for the VLAN interface configuration + file. + """ + + slave = _configure_pif(pif_get_vlan_slave(pif)) + + pifrec = db().get_pif_record(pif) + + f = open_pif_ifcfg(pif) + f.write("VLAN=yes\n") + + settings,offload = ethtool_settings(pifrec['other_config']) + if len(settings): + f.write("ETHTOOL_OPTS=\"%s\"\n" % str.join(" ", settings)) + if len(offload): + f.write("ETHTOOL_OFFLOAD_OPTS=\"%s\"\n" % str.join(" ", offload)) + + mtu = mtu_setting(pifrec['other_config']) + if mtu: + f.write("MTU=%s\n" % mtu) + + f.attach_child(slave) + + return f + +def _configure_pif(pif): + """Write the configuration for a PIF object. + + Writes the configuration file the PIF and all dependent + interfaces (bond slaves and VLAN masters etc). + + Returns the open file handle for the interface configuration file. + """ + + if pif_is_vlan(pif): + f = _configure_vlan_interface(pif) + elif pif_is_bond(pif): + f = _configure_bond_interface(pif) + else: + f = _configure_physical_interface(pif) + + f.write("BRIDGE=%s\n" % pif_bridge_name(pif)) + f.close() + + return f + +# +# +# + +class DatapathBridge(Datapath): + def __init__(self, pif): + Datapath.__init__(self, pif) + log("Configured for Bridge datapath") + + def configure_ipdev(self, cfg): + if pif_is_bridged(self._pif): + cfg.write("TYPE=Bridge\n") + cfg.write("DELAY=0\n") + cfg.write("STP=off\n") + cfg.write("PIFDEV=%s\n" % pif_netdev_name(self._pif)) + else: + cfg.write("TYPE=Ethernet\n") + + def preconfigure(self, parent): + pf = _configure_pif(self._pif) + parent.attach_child(pf) + + def bring_down_existing(self): + # Bring down any VLAN masters so that we can reconfigure the slave. + for master in pif_get_vlan_masters(self._pif): + name = pif_netdev_name(master) + log("action_up: bring down vlan master %s" % (name)) + netdev_down(name) + + # interface-reconfigure is never explicitly called to down a bond master. + # However, when we are called to up a slave it is implicit that we are destroying the master. + bond_masters = pif_get_bond_masters(self._pif) + for master in bond_masters: + log("action_up: bring down bond master %s" % (pif_netdev_name(master))) + # bring down master + bring_down_interface(master, destroy=True) + + # No masters left - now its safe to reconfigure the slave. + bring_down_interface(self._pif) + + def configure(self): + bring_up_interface(self._pif) + + def post(self): + # Bring back any currently-attached VLAN masters + for master in [v for v in pif_get_vlan_masters(self._pif) if db().get_pif_record(v)['currently_attached']]: + name = pif_netdev_name(master) + log("action_up: bring up %s" % (name)) + netdev_up(name) + + def bring_down(self): + bring_down_interface(self._pif, destroy=True) diff -r 28a62b543c4c -r 0e07bf7f3fe1 scripts/InterfaceReconfigureVswitch.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/InterfaceReconfigureVswitch.py Fri Dec 18 14:16:32 2009 +0000 @@ -0,0 +1,445 @@ +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): + # The use of [!0-9] keeps an interface of 'eth0' from matching + # VLANs attached to eth0 (such as 'eth0.123'), which are distinct + # interfaces. + return ['--del-match=bridge.*.port=%s' % netdev, + '--del-match=port.%s.[!0-9]*' % netdev, + '--del-match=bonding.*.slave=%s' % netdev, + '--del-match=iface.%s.[!0-9]*' % netdev] + +def datapath_configure_bond(pif,slaves): + pifrec = db().get_pif_record(pif) + interface = pif_netdev_name(pif) + + argv = ['--del-match=bonding.%s.[!0-9]*' % interface] + argv += ["--add=bonding.%s.slave=%s" % (interface, pif_netdev_name(slave)) + for slave in slaves] + argv += ['--add=bonding.%s.fake-iface=true' % interface] + + 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(): + argv += ["--add=bonding.%s.%s=%s" % (interface, name, val)] + return argv + +def datapath_deconfigure_bond(netdev): + # The use of [!0-9] keeps an interface of 'eth0' from matching + # VLANs attached to eth0 (such as 'eth0.123'), which are distinct + # interfaces. + return ['--del-match=bonding.%s.[!0-9]*' % netdev, + '--del-match=port.%s.[!0-9]*' % netdev] + +def datapath_deconfigure_ipdev(interface): + # The use of [!0-9] keeps an interface of 'eth0' from matching + # VLANs attached to eth0 (such as 'eth0.123'), which are distinct + # interfaces. + return ['--del-match=bridge.*.port=%s' % interface, + '--del-match=port.%s.[!0-9]*' % interface, + '--del-match=iface.%s.[!0-9]*' % interface, + '--del-match=vlan.%s.[!0-9]*' % interface] + +def datapath_modify_config(commands): + #log("modifying configuration:") + #for c in commands: + # log(" %s" % c) + + rc = run_command(['/usr/bin/ovs-cfg-mod', '-vANY:console:emer', + '-F', '/etc/ovs-vswitchd.conf'] + + [c for c in commands if c[0] != '#'] + ['-c']) + if not rc: + raise Error("Failed to modify vswitch configuration") + run_command(['/sbin/service', 'vswitch', 'reload']) + 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 cfgmod command line arguments + - A list of additional devices which should be brought up after + the configuration is applied. + """ + + cfgmod_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) + cfgmod_argv += ['# remove bridge %s' % b] + cfgmod_argv += ['--del-match=bridge.%s.*' % b] + + for n in extra_down_ports: + dev = pif_netdev_name(n) + cfgmod_argv += ['# deconfigure sibling physical device %s' % dev] + cfgmod_argv += datapath_deconfigure_physical(dev) + netdev_down(dev) + + for n in extra_down_bonds: + dev = pif_netdev_name(n) + cfgmod_argv += ['# deconfigure bond device %s' % dev] + cfgmod_argv += datapath_deconfigure_bond(dev) + netdev_down(dev) + + for p in physical_devices: + dev = pif_netdev_name(p) + cfgmod_argv += ['# deconfigure physical port %s' % dev] + cfgmod_argv += datapath_deconfigure_physical(dev) + + # Check the MAC address of each network device and remap if + # necessary to make names match our expectations. + # XXX done in main script + #for p in physical_devices: + # netdev_remap_name(p) + + # Bring up physical devices early, because 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". + 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) + + if len(physical_devices) > 1: + cfgmod_argv += ['# deconfigure bond %s' % pif_netdev_name(pif)] + cfgmod_argv += datapath_deconfigure_bond(pif_netdev_name(pif)) + cfgmod_argv += ['--del-entry=bridge.%s.port=%s' % (bridge,pif_netdev_name(pif))] + cfgmod_argv += ['# configure bond %s' % pif_netdev_name(pif)] + cfgmod_argv += datapath_configure_bond(pif, physical_devices) + cfgmod_argv += ['--add=bridge.%s.port=%s' % (bridge,pif_netdev_name(pif)) ] + extra_up_ports += [pif_netdev_name(pif)] + else: + iface = pif_netdev_name(physical_devices[0]) + cfgmod_argv += ['# add physical device %s' % iface] + cfgmod_argv += ['--add=bridge.%s.port=%s' % (bridge,iface) ] + + return cfgmod_argv,extra_up_ports + +def deconfigure_datapath(pif): + cfgmod_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) + cfgmod_argv += ['# deconfigure physical port %s' % dev] + cfgmod_argv += datapath_deconfigure_physical(dev) + netdev_down(dev) + + if len(physical_devices) > 1: + cfgmod_argv += ['# deconfigure bond %s' % pif_netdev_name(pif)] + cfgmod_argv += datapath_deconfigure_bond(pif_netdev_name(pif)) + + cfgmod_argv += ['# deconfigure bridge %s' % bridge] + cfgmod_argv += ['--del-match=bridge.%s.*' % bridge] + + return cfgmod_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): + cfgmod_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) + cfgmod_argv += c + extra_ports += e + + cfgmod_argv += ['# configure xs-network-uuids'] + cfgmod_argv += ['--del-match=bridge.%s.xs-network-uuids=*' % bridge] + + 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']) + cfgmod_argv += ['--add=bridge.%s.xs-network-uuids=%s' % (bridge, nwrec['uuid'])] + + cfgmod_argv += ["# deconfigure ipdev %s" % ipdev] + cfgmod_argv += datapath_deconfigure_ipdev(ipdev) + cfgmod_argv += ["# reconfigure ipdev %s" % ipdev] + cfgmod_argv += ['--add=bridge.%s.port=%s' % (bridge, ipdev)] + + if pif_is_vlan(self._pif): + cfgmod_argv += ['--add=vlan.%s.tag=%s' % (ipdev, pifrec['VLAN'])] + cfgmod_argv += ['--add=iface.%s.internal=true' % (ipdev)] + cfgmod_argv += ['--add=iface.%s.fake-bridge=true' % (ipdev)] + + self._cfgmod_argv = cfgmod_argv + self._extra_ports = extra_ports + + def bring_down_existing(self): + pass + + def configure(self): + datapath_modify_config(self._cfgmod_argv) + + def post(self): + for p in self._extra_ports: + log("action_up: bring up %s" % p) + netdev_up(p) + + def bring_down(self): + cfgmod_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) + #cfgmod_argv += ['# deconfigure xs-network-uuids'] + #cfgmod_argv += ['--del-entry=bridge.%s.xs-network-uuids=%s' % (bridge,nwrec['uuid'])] + + log("deconfigure ipdev %s on %s" % (ipdev,bridge)) + cfgmod_argv += ["# deconfigure ipdev %s" % ipdev] + cfgmod_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: + cfgmod_argv += deconfigure_datapath(dp) + datapath_modify_config(cfgmod_argv) diff -r 28a62b543c4c -r 0e07bf7f3fe1 scripts/OMakefile --- a/scripts/OMakefile Fri Dec 18 14:16:32 2009 +0000 +++ b/scripts/OMakefile Fri Dec 18 14:16:32 2009 +0000 @@ -69,6 +69,8 @@ mkdir -p $(DIST)/staging/opt/xensource/packages/iso #omg XXX $(IPROG) interface-reconfigure $(LIBEXEC) $(IPROG) InterfaceReconfigure.py $(LIBEXEC) + $(IPROG) InterfaceReconfigureBridge.py $(LIBEXEC) + $(IPROG) InterfaceReconfigureVswitch.py $(LIBEXEC) $(IPROG) rewrite-management-interface $(LIBEXEC) $(IPROG) interface-visualise $(LIBEXEC) $(IPROG) logrotate.sh $(LIBEXEC) diff -r 28a62b543c4c -r 0e07bf7f3fe1 scripts/interface-reconfigure --- a/scripts/interface-reconfigure Fri Dec 18 14:16:32 2009 +0000 +++ b/scripts/interface-reconfigure Fri Dec 18 14:16:32 2009 +0000 @@ -46,13 +46,11 @@ import os, sys, getopt import syslog import traceback -import time import re import random management_pif = None -sysfs_bonding_masters = "/sys/class/net/bonding_masters" dbcache_file = "/var/xapi/network.dbcache" # @@ -70,18 +68,6 @@ log("%(message)s: %(pif_netdev_name)s configured as %(ip_configuration_mode)s" % rec) # -# Run external utilities -# - -def run_command(command): - log("Running command: " + ' '.join(command)) - rc = os.spawnl(os.P_WAIT, command[0], *command) - if rc != 0: - log("Command failed %d: " % rc + ' '.join(command)) - return False - return True - -# # Exceptions. # @@ -89,149 +75,6 @@ def __init__(self, msg): Exception.__init__(self) self.msg = msg - -# -# Configuration File Handling. -# - -class ConfigurationFile(object): - """Write a file, tracking old and new versions. - - Supports writing a new version of a file and applying and - reverting those changes. - """ - - __STATE = {"OPEN":"OPEN", - "NOT-APPLIED":"NOT-APPLIED", "APPLIED":"APPLIED", - "REVERTED":"REVERTED", "COMMITTED": "COMMITTED"} - - def __init__(self, path): - dirname,basename = os.path.split(path) - - self.__state = self.__STATE['OPEN'] - self.__children = [] - - self.__path = os.path.join(dirname, basename) - self.__oldpath = os.path.join(dirname, "." + basename + ".xapi-old") - self.__newpath = os.path.join(dirname, "." + basename + ".xapi-new") - - self.__f = open(self.__newpath, "w") - - def attach_child(self, child): - self.__children.append(child) - - def path(self): - return self.__path - - def readlines(self): - try: - return open(self.path()).readlines() - except: - return "" - - def write(self, args): - if self.__state != self.__STATE['OPEN']: - raise Error("Attempt to write to file in state %s" % self.__state) - self.__f.write(args) - - def close(self): - if self.__state != self.__STATE['OPEN']: - raise Error("Attempt to close file in state %s" % self.__state) - - self.__f.close() - self.__state = self.__STATE['NOT-APPLIED'] - - def changed(self): - if self.__state != self.__STATE['NOT-APPLIED']: - raise Error("Attempt to compare file in state %s" % self.__state) - - return True - - def apply(self): - if self.__state != self.__STATE['NOT-APPLIED']: - raise Error("Attempt to apply configuration from state %s" % self.__state) - - for child in self.__children: - child.apply() - - log("Applying changes to %s configuration" % self.__path) - - # Remove previous backup. - if os.access(self.__oldpath, os.F_OK): - os.unlink(self.__oldpath) - - # Save current configuration. - if os.access(self.__path, os.F_OK): - os.link(self.__path, self.__oldpath) - os.unlink(self.__path) - - # Apply new configuration. - assert(os.path.exists(self.__newpath)) - os.link(self.__newpath, self.__path) - - # Remove temporary file. - os.unlink(self.__newpath) - - self.__state = self.__STATE['APPLIED'] - - def revert(self): - if self.__state != self.__STATE['APPLIED']: - raise Error("Attempt to revert configuration from state %s" % self.__state) - - for child in self.__children: - child.revert() - - log("Reverting changes to %s configuration" % self.__path) - - # Remove existing new configuration - if os.access(self.__newpath, os.F_OK): - os.unlink(self.__newpath) - - # Revert new configuration. - if os.access(self.__path, os.F_OK): - os.link(self.__path, self.__newpath) - os.unlink(self.__path) - - # Revert to old configuration. - if os.access(self.__oldpath, os.F_OK): - os.link(self.__oldpath, self.__path) - os.unlink(self.__oldpath) - - # Leave .*.xapi-new as an aid to debugging. - - self.__state = self.__STATE['REVERTED'] - - def commit(self): - if self.__state != self.__STATE['APPLIED']: - raise Error("Attempt to commit configuration from state %s" % self.__state) - - for child in self.__children: - child.commit() - - log("Committing changes to %s configuration" % self.__path) - - if os.access(self.__oldpath, os.F_OK): - os.unlink(self.__oldpath) - if os.access(self.__newpath, os.F_OK): - os.unlink(self.__newpath) - - self.__state = self.__STATE['COMMITTED'] - -def open_pif_ifcfg(pif): - pifrec = db().get_pif_record(pif) - - interface = pif_netdev_name(pif) - log("Configuring %s (%s)" % (interface, pifrec['MAC'])) - - f = ConfigurationFile("/etc/sysconfig/network-scripts/ifcfg-%s" % interface) - - f.write("# DO NOT EDIT: This file (%s) was autogenerated by %s\n" % \ - (os.path.basename(f.path()), os.path.basename(sys.argv[0]))) - f.write("XEMANAGED=yes\n") - f.write("DEVICE=%s\n" % interface) - f.write("ONBOOT=no\n") - - return f # # Boot from Network filesystem or device. @@ -260,33 +103,6 @@ # # Bare Network Devices -- network devices without IP configuration # - -def netdev_exists(netdev): - return os.path.exists("/sys/class/net/" + netdev) - -def pif_netdev_name(pif): - """Get the netdev name for a PIF.""" - - pifrec = db().get_pif_record(pif) - - if pif_is_vlan(pif): - return "%(device)s.%(VLAN)s" % pifrec - else: - return pifrec['device'] - -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/ifdown", netdev]) - -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) - - run_command(["/sbin/ifup", netdev]) def netdev_remap_name(pif, already_renamed=[]): """Check whether 'pif' exists and has the correct MAC. @@ -364,18 +180,6 @@ # IP Network Devices -- network devices with IP configuration # -def pif_ipdev_name(pif): - """Return the ipdev name associated with pif""" - pifrec = db().get_pif_record(pif) - nwrec = db().get_network_record(pifrec['network']) - - if nwrec['bridge']: - # TODO: sanity check that nwrec['bridgeless'] != 'true' - return nwrec['bridge'] - else: - # TODO: sanity check that nwrec['bridgeless'] == 'true' - return pif_netdev_name(pif) - def ifdown(netdev): """Bring down a network interface""" if not netdev_exists(netdev): @@ -388,271 +192,8 @@ run_command(["/sbin/ifup", netdev]) # -# Bridges # - -def pif_is_bridged(pif): - pifrec = db().get_pif_record(pif) - nwrec = db().get_network_record(pifrec['network']) - - if nwrec['bridge']: - # TODO: sanity check that nwrec['bridgeless'] != 'true' - return True - else: - # TODO: sanity check that nwrec['bridgeless'] == 'true' - return False - -def pif_bridge_name(pif): - """Return the bridge name of a pif. - - PIF must be a bridged PIF.""" - pifrec = db().get_pif_record(pif) - - 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) - -def load_bonding_driver(): - log("Loading bonding driver") - run_command(["/sbin/modprobe", "bonding"]) - try: - # bond_device_exists() uses the contents of sysfs_bonding_masters to work out which devices - # have already been created. Unfortunately the driver creates "bond0" automatically at - # modprobe init. Get rid of this now or our accounting will go wrong. - f = open(sysfs_bonding_masters, "w") - f.write("-bond0") - f.close() - except IOError, e: - log("Failed to load bonding driver: %s" % e) - -def bonding_driver_loaded(): - lines = open("/proc/modules").read().split("\n") - modules = [line.split(" ")[0] for line in lines] - return "bonding" in modules - -def bond_device_exists(name): - f = open(sysfs_bonding_masters, "r") - bonds = f.readline().split() - f.close() - return name in bonds - -def __create_bond_device(name): - - if not bonding_driver_loaded(): - load_bonding_driver() - - if bond_device_exists(name): - log("bond master %s already exists, not creating" % name) - else: - log("Creating bond master %s" % name) - try: - f = open(sysfs_bonding_masters, "w") - f.write("+" + name) - f.close() - except IOError, e: - log("Failed to create %s: %s" % (name, e)) - -def create_bond_device(pif): - """Ensures that a bond master device exists in the kernel.""" - - if not pif_is_bond(pif): - return - - __create_bond_device(pif_netdev_name(pif)) - -def __destroy_bond_device(name): - if bond_device_exists(name): - retries = 10 # 10 * 0.5 seconds - while retries > 0: - retries = retries - 1 - log("Destroying bond master %s (%d attempts remain)" % (name,retries)) - try: - f = open(sysfs_bonding_masters, "w") - f.write("-" + name) - f.close() - retries = 0 - except IOError, e: - time.sleep(0.5) - else: - log("bond master %s does not exist, not destroying" % name) - -def destroy_bond_device(pif): - """No, Mr. Bond, I expect you to die.""" - - pifrec = db().get_pif_record(pif) - - if not pif_is_bond(pif): - return - - # If the bonding module isn't loaded then do nothing. - if not os.access(sysfs_bonding_masters, os.F_OK): - return - - name = pif_netdev_name(pif) - - __destroy_bond_device(name) - -def configure_physical_interface(pif): - """Write the configuration for a physical interface. - - Writes the configuration file for the physical interface described by - the pif object. - - Returns the open file handle for the interface configuration file. - """ - - pifrec = db().get_pif_record(pif) - - f = open_pif_ifcfg(pif) - - f.write("TYPE=Ethernet\n") - f.write("HWADDR=%(MAC)s\n" % pifrec) - - settings,offload = ethtool_settings(pifrec['other_config']) - if len(settings): - f.write("ETHTOOL_OPTS=\"%s\"\n" % str.join(" ", settings)) - if len(offload): - f.write("ETHTOOL_OFFLOAD_OPTS=\"%s\"\n" % str.join(" ", offload)) - - mtu = mtu_setting(pifrec['other_config']) - if mtu: - f.write("MTU=%s\n" % mtu) - - return f - -def pif_get_bond_slaves_sorted(pif): - pifrec = db().get_pif_record(pif) - - # build a list of slave's pifs - slave_pifs = pif_get_bond_slaves(pif) - - # Ensure any currently attached slaves are listed in the opposite order to the order in - # which they were attached. The first slave attached must be the last detached since - # the bond is using its MAC address. - try: - attached_slaves = open("/sys/class/net/%s/bonding/slaves" % pifrec['device']).readline().split() - for slave in attached_slaves: - pifs = [p for p in db().get_pifs_by_device(slave) if not pif_is_vlan(p)] - slave_pif = pifs[0] - slave_pifs.remove(slave_pif) - slave_pifs.insert(0, slave_pif) - except IOError: - pass - - return slave_pifs - -def configure_bond_interface(pif): - """Write the configuration for a bond interface. - - Writes the configuration file for the bond interface described by - the pif object. Handles writing the configuration for the slave - interfaces. - - Returns the open file handle for the bond interface configuration - file. - """ - - pifrec = db().get_pif_record(pif) - - f = open_pif_ifcfg(pif) - - if pifrec['MAC'] != "": - f.write("MACADDR=%s\n" % pifrec['MAC']) - - for slave in pif_get_bond_slaves(pif): - s = configure_physical_interface(slave) - s.write("MASTER=%(device)s\n" % pifrec) - s.write("SLAVE=yes\n") - s.close() - f.attach_child(s) - - settings,offload = ethtool_settings(pifrec['other_config']) - if len(settings): - f.write("ETHTOOL_OPTS=\"%s\"\n" % str.join(" ", settings)) - if len(offload): - f.write("ETHTOOL_OFFLOAD_OPTS=\"%s\"\n" % str.join(" ", offload)) - - mtu = mtu_setting(pifrec['other_config']) - if mtu: - f.write("MTU=%s\n" % mtu) - - # The bond option defaults - 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) - - # write the bond options to ifcfg-bondX - f.write('BONDING_OPTS="') - for (name,val) in bond_options.items(): - f.write("%s=%s " % (name,val)) - f.write('"\n') - return f - -def configure_vlan_interface(pif): - """Write the configuration for a VLAN interface. - - Writes the configuration file for the VLAN interface described by - the pif object. Handles writing the configuration for the master - interface if necessary. - - Returns the open file handle for the VLAN interface configuration - file. - """ - - slave = configure_pif(pif_get_vlan_slave(pif)) - - pifrec = db().get_pif_record(pif) - - f = open_pif_ifcfg(pif) - f.write("VLAN=yes\n") - - settings,offload = ethtool_settings(pifrec['other_config']) - if len(settings): - f.write("ETHTOOL_OPTS=\"%s\"\n" % str.join(" ", settings)) - if len(offload): - f.write("ETHTOOL_OFFLOAD_OPTS=\"%s\"\n" % str.join(" ", offload)) - - mtu = mtu_setting(pifrec['other_config']) - if mtu: - f.write("MTU=%s\n" % mtu) - - f.attach_child(slave) - - return f - -def configure_pif(pif): - """Write the configuration for a PIF object. - - Writes the configuration file the PIF and all dependent - interfaces (bond slaves and VLAN masters etc). - - Returns the open file handle for the interface configuration file. - """ - - if pif_is_vlan(pif): - f = configure_vlan_interface(pif) - elif pif_is_bond(pif): - f = configure_bond_interface(pif) - else: - f = configure_physical_interface(pif) - - f.write("BRIDGE=%s\n" % pif_bridge_name(pif)) - f.close() - - return f +# def pif_rename_physical_devices(pif): @@ -666,118 +207,6 @@ for pif in pifs: netdev_remap_name(pif) - -def bring_down_interface(pif, destroy=False): - """Bring down the interface associated with PIF. - - Brings down the given interface as well as any physical interfaces - which are bond slaves of this one. This is because they will be - required when the bond is brought up.""" - - def destroy_bridge(pif): - """Bring down the bridge associated with a PIF.""" - if not pif_is_bridged(pif): - return - bridge = pif_bridge_name(pif) - if not netdev_exists(bridge): - log("destroy_bridge: bridge %s does not exist, ignoring" % bridge) - return - log("Destroy bridge %s" % bridge) - netdev_down(bridge) - run_command(["/usr/sbin/brctl", "delbr", bridge]) - - def destroy_vlan(pif): - vlan = pif_netdev_name(pif) - if not netdev_exists(vlan): - log("vconfig del: vlan %s does not exist, ignoring" % vlan) - return - log("Destroy vlan device %s" % vlan) - run_command(["/sbin/vconfig", "rem", vlan]) - - if pif_is_vlan(pif): - interface = pif_netdev_name(pif) - log("bring_down_interface: %s is a VLAN" % interface) - netdev_down(interface) - - if destroy: - destroy_vlan(pif) - destroy_bridge(pif) - else: - return - - slave = pif_get_vlan_slave(pif) - if db().get_pif_record(slave)['currently_attached']: - log("bring_down_interface: vlan slave is currently attached") - return - - masters = pif_get_vlan_masters(slave) - masters = [m for m in masters if m != pif and db().get_pif_record(m)['currently_attached']] - if len(masters) > 0: - log("bring_down_interface: vlan slave has other masters") - return - - log("bring_down_interface: no more masters, bring down vlan slave %s" % pif_netdev_name(slave)) - pif = slave - else: - vlan_masters = pif_get_vlan_masters(pif) - log("vlan masters of %s - %s" % (db().get_pif_record(pif)['device'], [pif_netdev_name(m) for m in vlan_masters])) - if len([m for m in vlan_masters if db().get_pif_record(m)['currently_attached']]) > 0: - log("Leaving %s up due to currently attached VLAN masters" % pif_netdev_name(pif)) - return - - # pif is now either a bond or a physical device which needs to be brought down - - # Need to bring down bond slaves first since the bond device - # must be up to enslave/unenslave. - bond_slaves = pif_get_bond_slaves_sorted(pif) - log("bond slaves of %s - %s" % (db().get_pif_record(pif)['device'], [pif_netdev_name(s) for s in bond_slaves])) - for slave in bond_slaves: - slave_interface = pif_netdev_name(slave) - if db().get_pif_record(slave)['currently_attached']: - log("leave bond slave %s up (currently attached)" % slave_interface) - continue - log("bring down bond slave %s" % slave_interface) - netdev_down(slave_interface) - # Also destroy the bridge associated with the slave, since - # it will carry the MAC address and possibly an IP address - # leading to confusion. - destroy_bridge(slave) - - interface = pif_netdev_name(pif) - log("Bring interface %s down" % interface) - netdev_down(interface) - - if destroy: - destroy_bond_device(pif) - destroy_bridge(pif) - -def interface_is_up(pif): - try: - interface = pif_netdev_name(pif) - state = open("/sys/class/net/%s/operstate" % interface).read().strip() - return state == "up" - except: - return False # interface prolly doesn't exist - -def bring_up_interface(pif): - """Bring up the interface associated with a PIF. - - Also bring up the interfaces listed in additional. - """ - - # VLAN on bond seems to need bond brought up explicitly, but VLAN - # on normal device does not. Might as well always bring it up. - if pif_is_vlan(pif): - slave = pif_get_vlan_slave(pif) - if not interface_is_up(slave): - bring_up_interface(slave) - - interface = pif_netdev_name(pif) - - create_bond_device(pif) - - log("Bring interface %s up" % interface) - netdev_up(interface) # # IP device configuration @@ -833,7 +262,7 @@ return f -def ipdev_configure_network(pif): +def ipdev_configure_network(pif, dp): """Write the configuration file for a network. Writes configuration derived from the network object into the relevant @@ -845,6 +274,7 @@ params: pif: Opaque_ref of pif + dp: Datapath object """ pifrec = db().get_pif_record(pif) @@ -861,13 +291,7 @@ if pifrec.has_key('other_config'): oc = pifrec['other_config'] - if pif_is_bridged(pif): - f.write("TYPE=Bridge\n") - f.write("DELAY=0\n") - f.write("STP=off\n") - f.write("PIFDEV=%s\n" % pif_netdev_name(pif)) - else: - f.write("TYPE=Ethernet\n") + dp.configure_ipdev(f) if pifrec['ip_configuration_mode'] == "DHCP": f.write("BOOTPROTO=dhcp\n") @@ -974,23 +398,6 @@ return f # -# Datapath Configuration -# - -def pif_datapath(pif): - """Return the datapath PIF associated with 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. -""" - pifrec = db().get_pif_record(pif) - nwrec = db().get_network_record(pifrec['network']) - if not nwrec['bridge']: - return None - else: - return pif - - -# # Toplevel actions # @@ -998,18 +405,16 @@ pifrec = db().get_pif_record(pif) ipdev = pif_ipdev_name(pif) - dp = pif_datapath(pif) + dp = DatapathFactory(pif) log("action_up: %s" % ipdev) - f = ipdev_configure_network(pif) + f = ipdev_configure_network(pif, dp) - if dp: - pf = configure_pif(dp) - f.attach_child(pf) + dp.preconfigure(f) f.close() - + pif_rename_physical_devices(pif) # if we are not forcing the interface up then attempt to tear down @@ -1018,36 +423,16 @@ if not force: ifdown(ipdev) - # Bring down any VLAN masters so that we can reconfigure the slave. - for master in pif_get_vlan_masters(pif): - name = pif_netdev_name(master) - log("action_up: bring down vlan master %s" % (name)) - netdev_down(name) - - # interface-reconfigure is never explicitly called to down a bond master. - # However, when we are called to up a slave it is implicit that we are destroying the master. - bond_masters = pif_get_bond_masters(pif) - for master in bond_masters: - log("action_up: bring down bond master %s" % (pif_netdev_name(master))) - # bring down master - bring_down_interface(master, destroy=True) - - # No masters left - now its safe to reconfigure the slave. - bring_down_interface(pif) + dp.bring_down_existing() try: f.apply() - if dp: - bring_up_interface(pif) + dp.configure() ifup(ipdev) - # Bring back any currently-attached VLAN masters (brought down above) - for master in [v for v in pif_get_vlan_masters(pif) if db().get_pif_record(v)['currently_attached']]: - name = pif_netdev_name(master) - log("action_up: bring up %s" % (name)) - netdev_up(name) + dp.post() # Update /etc/issue (which contains the IP address of the management interface) os.system("/sbin/update-issue") @@ -1060,14 +445,13 @@ def action_down(pif): ipdev = pif_ipdev_name(pif) - dp = pif_datapath(pif) + dp = DatapathFactory(pif) log("action_down: %s" % ipdev) ifdown(ipdev) - if dp: - bring_down_interface(dp, destroy=True) + dp.bring_down() # This is useful for reconfiguring the mgmt interface after having lost connectivity to the pool master def action_force_rewrite(bridge, config): _______________________________________________ xen-api mailing list xen-api@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/mailman/listinfo/xen-api
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |