[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


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.