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
|