Drop separate vswitch-cfg-update.log.
[openvswitch] / xenserver / opt_xensource_libexec_interface-reconfigure
index 2a32fad275b3fe0ff700ab79e7facd3f44a97382..eae198b914823d23bdc819922b5e638efe4861d6 100755 (executable)
@@ -1,26 +1,30 @@
 #!/usr/bin/python
 #
 #!/usr/bin/python
 #
-# Copyright (c) Citrix Systems 2008. All rights reserved.
+# Copyright (c) 2008,2009 Citrix Systems, Inc. All rights reserved.
 # Copyright (c) 2009 Nicira Networks.
 #
 """Usage:
 
 # Copyright (c) 2009 Nicira Networks.
 #
 """Usage:
 
-    %(command-name)s --session <SESSION-REF> --pif <PIF-REF> [up|down|rewrite]
-    %(command-name)s --force <BRIDGE> [up|down|rewrite <CONFIG>]
+    %(command-name)s <PIF> up
+    %(command-name)s <PIF> down
+    %(command-name)s [<PIF>] rewrite
+    %(command-name)s --force <BRIDGE> up
+    %(command-name)s --force <BRIDGE> down
+    %(command-name)s --force <BRIDGE> rewrite --device=<INTERFACE> <CONFIG>
     %(command-name)s --force all down
 
     %(command-name)s --force all down
 
-    where,
-          <CONFIG> = --device=<INTERFACE> --mode=dhcp
-          <CONFIG> = --device=<INTERFACE> --mode=static --ip=<IPADDR> --netmask=<NM> [--gateway=<GW>]
+    where <PIF> is one of:
+       --session <SESSION-REF> --pif <PIF-REF>
+       --pif-uuid <PIF-UUID>
+    and <CONFIG> is one of:
+       --mode=dhcp
+       --mode=static --ip=<IPADDR> --netmask=<NM> [--gateway=<GW>]
 
   Options:
     --session          A session reference to use to access the xapi DB
 
   Options:
     --session          A session reference to use to access the xapi DB
-    --pif               A PIF reference.
-    --force-interface  An interface name. Mutually exclusive with --session/--pif.
-
-  Either both --session and --pif  or just --pif-uuid.
-  
-  <ACTION> is either "up" or "down" or "rewrite"
+    --pif               A PIF reference within the session.
+    --pif-uuid          The UUID of a PIF.
+    --force             An interface name.
 """
 
 #
 """
 
 #
@@ -28,7 +32,6 @@
 #
 #  --output-directory=<DIR>    Write configuration to <DIR>. Also disables actually
 #                               raising/lowering the interfaces
 #
 #  --output-directory=<DIR>    Write configuration to <DIR>. Also disables actually
 #                               raising/lowering the interfaces
-#  --pif-uuid                  A PIF UUID, use instead of --session/--pif.
 #
 #
 #
 #
 #
 #
 # 3. A network may have an associated bridge, allowing vifs to be attached
 # 4. A network may be bridgeless (there's no point having a bridge over a storage pif)
 
 # 3. A network may have an associated bridge, allowing vifs to be attached
 # 4. A network may be bridgeless (there's no point having a bridge over a storage pif)
 
-# XXX: --force-interface=all down
-
-# XXX: --force-interface rewrite
-
-# XXX: Sometimes this leaves "orphaned" datapaths, e.g. a datapath whose
-#      only port is the local port.  Should delete those.
-
-# XXX: This can leave crud in ovs-vswitchd.conf in this scenario:
-#      - Create bond in XenCenter.
-#      - Create VLAN on bond in XenCenter.
-#      - Attempt to delete bond in XenCenter (this will fail because there
-#        is a VLAN on the bond, although the error may not be reported
-#        until the next step)
-#      - Delete VLAN in XenCenter.
-#      - Delete bond in XenCenter.
-# At this point there will still be some configuration data for the bond
-# or the VLAN in ovs-vswitchd.conf.
-
 import XenAPI
 import os, sys, getopt, time, signal
 import syslog
 import traceback
 import time
 import re
 import XenAPI
 import os, sys, getopt, time, signal
 import syslog
 import traceback
 import time
 import re
-import pickle
+import random
+from xml.dom.minidom import getDOMImplementation
+from xml.dom.minidom import parse as parseXML
 
 output_directory = None
 
 db = None
 management_pif = None
 
 
 output_directory = None
 
 db = None
 management_pif = None
 
-dbcache_file = "/etc/ovs-vswitch.dbcache"
-vswitch_config_dir = "/etc/openvswitch"
+vswitch_state_dir = "/var/lib/openvswitch/"
+dbcache_file = vswitch_state_dir + "dbcache"
 
 class Usage(Exception):
     def __init__(self, msg):
 
 class Usage(Exception):
     def __init__(self, msg):
@@ -248,11 +235,248 @@ def check_allowed(pif):
 def interface_exists(i):
     return os.path.exists("/sys/class/net/" + i)
 
 def interface_exists(i):
     return os.path.exists("/sys/class/net/" + i)
 
+def get_netdev_mac(device):
+    try:
+        return read_first_line_of_file("/sys/class/net/%s/address" % device)
+    except:
+        # Probably no such device.
+        return None
+
+def get_netdev_tx_queue_len(device):
+    try:
+        return int(read_first_line_of_file("/sys/class/net/%s/tx_queue_len"
+                                           % device))
+    except:
+        # Probably no such device.
+        return None
+
+def get_netdev_by_mac(mac):
+    for device in os.listdir("/sys/class/net"):
+        dev_mac = get_netdev_mac(device)
+        if (dev_mac and mac.lower() == dev_mac.lower() and
+            get_netdev_tx_queue_len(device)):
+                return device
+    return None
+
+#
+# Helper functions for encoding/decoding database attributes to/from XML.
+#
+def str_to_xml(xml, parent, tag, val):
+    e = xml.createElement(tag)
+    parent.appendChild(e)
+    v = xml.createTextNode(val)
+    e.appendChild(v)
+def str_from_xml(n):
+    def getText(nodelist):
+        rc = ""
+        for node in nodelist:
+            if node.nodeType == node.TEXT_NODE:
+                rc = rc + node.data
+        return rc
+    return getText(n.childNodes).strip()
+
+
+def bool_to_xml(xml, parent, tag, val):
+    if val:
+        str_to_xml(xml, parent, tag, "True")
+    else:
+        str_to_xml(xml, parent, tag, "False")
+def bool_from_xml(n):
+    s = str_from_xml(n)
+    if s == "True":
+        return True
+    elif s == "False":
+        return False
+    else:
+        raise Error("Unknown boolean value %s" % s);
+
+def strlist_to_xml(xml, parent, ltag, itag, val):
+    e = xml.createElement(ltag)
+    parent.appendChild(e)
+    for v in val:
+        c = xml.createElement(itag)
+        e.appendChild(c)
+        cv = xml.createTextNode(v)
+        c.appendChild(cv)
+def strlist_from_xml(n, ltag, itag):
+    ret = []
+    for n in n.childNodes:
+        if n.nodeName == itag:
+            ret.append(str_from_xml(n))
+    return ret
+
+def otherconfig_to_xml(xml, parent, val, attrs):
+    otherconfig = xml.createElement("other_config")
+    parent.appendChild(otherconfig)
+    for n,v in val.items():
+        if not n in attrs:
+            raise Error("Unknown other-config attribute: %s" % n)
+        str_to_xml(xml, otherconfig, n, v)
+def otherconfig_from_xml(n, attrs):
+    ret = {}
+    for n in n.childNodes:
+        if n.nodeName in attrs:
+            ret[n.nodeName] = str_from_xml(n)
+    return ret
+
+#
+# Definitions of the database objects (and their attributes) used by interface-reconfigure.
+#
+# Each object is defined by a dictionary mapping an attribute name in
+# the xapi database to a tuple containing two items:
+#  - a function which takes this attribute and encodes it as XML.
+#  - a function which takes XML and decocdes it into a value.
+#
+# other-config attributes are specified as a simple array of strings
+
+PIF_XML_TAG = "pif"
+VLAN_XML_TAG = "vlan"
+BOND_XML_TAG = "bond"
+NETWORK_XML_TAG = "network"
+
+ETHTOOL_OTHERCONFIG_ATTRS = ['ethtool-%s' % x for x in 'autoneg', 'speed', 'duplex', 'rx', 'tx', 'sg', 'tso', 'ufo', 'gso' ]
+
+PIF_ATTRS = { 'uuid': (str_to_xml,str_from_xml),
+              'management': (bool_to_xml,bool_from_xml),
+              'network': (str_to_xml,str_from_xml),
+              'device': (str_to_xml,str_from_xml),
+              'bond_master_of': (lambda x, p, t, v: strlist_to_xml(x, p, 'bond_master_of', 'slave', v),
+                                 lambda n: strlist_from_xml(n, 'bond_master_of', 'slave')),
+              'bond_slave_of': (str_to_xml,str_from_xml),
+              'VLAN': (str_to_xml,str_from_xml),
+              'VLAN_master_of': (str_to_xml,str_from_xml),
+              'VLAN_slave_of': (lambda x, p, t, v: strlist_to_xml(x, p, 'VLAN_slave_of', 'master', v),
+                                lambda n: strlist_from_xml(n, 'VLAN_slave_Of', 'master')),
+              'ip_configuration_mode': (str_to_xml,str_from_xml),
+              'IP': (str_to_xml,str_from_xml),
+              'netmask': (str_to_xml,str_from_xml),
+              'gateway': (str_to_xml,str_from_xml),
+              'DNS': (str_to_xml,str_from_xml),
+              'MAC': (str_to_xml,str_from_xml),
+              'other_config': (lambda x, p, t, v: otherconfig_to_xml(x, p, v, PIF_OTHERCONFIG_ATTRS),
+                               lambda n: otherconfig_from_xml(n, PIF_OTHERCONFIG_ATTRS)),
+              
+              # Special case: We write the current value
+              # PIF.currently-attached to the cache but since it will
+              # not be valid when we come to use the cache later
+              # (i.e. after a reboot) we always read it as False.
+              'currently_attached': (bool_to_xml, lambda n: False),
+            }
+
+PIF_OTHERCONFIG_ATTRS = [ 'domain', 'peerdns', 'defaultroute', 'mtu', 'static-routes' ] + \
+                        [ 'bond-%s' % x for x in 'mode', 'miimon', 'downdelay', 'updelay', 'use_carrier' ] + \
+                        ETHTOOL_OTHERCONFIG_ATTRS
+
+VLAN_ATTRS = { 'uuid': (str_to_xml,str_from_xml),
+               'tagged_PIF': (str_to_xml,str_from_xml),
+               'untagged_PIF': (str_to_xml,str_from_xml),
+             }
+    
+BOND_ATTRS = { 'uuid': (str_to_xml,str_from_xml),
+               'master': (str_to_xml,str_from_xml),
+               'slaves': (lambda x, p, t, v: strlist_to_xml(x, p, 'slaves', 'slave', v),
+                          lambda n: strlist_from_xml(n, 'slaves', 'slave')),
+             }
+
+NETWORK_ATTRS = { 'uuid': (str_to_xml,str_from_xml),
+                  'bridge': (str_to_xml,str_from_xml),
+                  'PIFs': (lambda x, p, t, v: strlist_to_xml(x, p, 'PIFs', 'PIF', v),
+                           lambda n: strlist_from_xml(n, 'PIFs', 'PIF')),
+                  'other_config': (lambda x, p, t, v: otherconfig_to_xml(x, p, v, NETWORK_OTHERCONFIG_ATTRS),
+                                   lambda n: otherconfig_from_xml(n, NETWORK_OTHERCONFIG_ATTRS)),
+                }
+
+NETWORK_OTHERCONFIG_ATTRS = [ 'mtu', 'static-routes' ] + ETHTOOL_OTHERCONFIG_ATTRS
+
 class DatabaseCache(object):
 class DatabaseCache(object):
+    def __read_xensource_inventory(self):
+        filename = "/etc/xensource-inventory"
+        f = open(filename, "r")
+        lines = [x.strip("\n") for x in f.readlines()]
+        f.close()
+
+        defs = [ (l[:l.find("=")], l[(l.find("=") + 1):]) for l in lines ]
+        defs = [ (a, b.strip("'")) for (a,b) in defs ]
+
+        return dict(defs)
+    def __pif_on_host(self,pif):
+        return self.__pifs.has_key(pif)
+
+    def __get_pif_records_from_xapi(self, session, host):
+        self.__pifs = {}
+        for (p,rec) in session.xenapi.PIF.get_all_records().items():
+            if rec['host'] != host:
+                continue
+            self.__pifs[p] = {}
+            for f in PIF_ATTRS:
+                self.__pifs[p][f] = rec[f]
+            self.__pifs[p]['other_config'] = {}
+            for f in PIF_OTHERCONFIG_ATTRS:
+                if not rec['other_config'].has_key(f): continue
+                self.__pifs[p]['other_config'][f] = rec['other_config'][f]
+
+    def __get_vlan_records_from_xapi(self, session):
+        self.__vlans = {}
+        for v in session.xenapi.VLAN.get_all():
+            rec = session.xenapi.VLAN.get_record(v)
+            if not self.__pif_on_host(rec['untagged_PIF']):
+                continue
+            self.__vlans[v] = {}
+            for f in VLAN_ATTRS:
+                self.__vlans[v][f] = rec[f]
+
+    def __get_bond_records_from_xapi(self, session):
+        self.__bonds = {}
+        for b in session.xenapi.Bond.get_all():
+            rec = session.xenapi.Bond.get_record(b)
+            if not self.__pif_on_host(rec['master']):
+                continue
+            self.__bonds[b] = {}
+            for f in BOND_ATTRS:
+                self.__bonds[b][f] = rec[f]
+
+    def __get_network_records_from_xapi(self, session):
+        self.__networks = {}
+        for n in session.xenapi.network.get_all():
+            rec = session.xenapi.network.get_record(n)
+            self.__networks[n] = {}
+            for f in NETWORK_ATTRS:
+                if f == "PIFs":
+                    # drop PIFs on other hosts
+                    self.__networks[n][f] = [p for p in rec[f] if self.__pif_on_host(p)]
+                else:
+                    self.__networks[n][f] = rec[f]
+            self.__networks[n]['other_config'] = {}
+            for f in NETWORK_OTHERCONFIG_ATTRS:
+                if not rec['other_config'].has_key(f): continue
+                self.__networks[n]['other_config'][f] = rec['other_config'][f]
+
+    def __to_xml(self, xml, parent, key, ref, rec, attrs):
+        """Encode a database object as XML"""
+        e = xml.createElement(key)
+        parent.appendChild(e)
+        if ref:
+            e.setAttribute('ref', ref)
+
+        for n,v in rec.items():
+            if attrs.has_key(n):
+                h,_ = attrs[n]
+                h(xml, e, n, v)
+            else:
+                raise Error("Unknown attribute %s" % n)
+    def __from_xml(self, e, attrs):
+        """Decode a database object from XML"""
+        ref = e.attributes['ref'].value
+        rec = {}
+        for n in e.childNodes:
+            if n.nodeName in attrs:
+                _,h = attrs[n.nodeName]
+                rec[n.nodeName] = h(n)
+        return (ref,rec)
+    
     def __init__(self, session_ref=None, cache_file=None):
         if session_ref and cache_file:
             raise Error("can't specify session reference and cache file")
     def __init__(self, session_ref=None, cache_file=None):
         if session_ref and cache_file:
             raise Error("can't specify session reference and cache file")
-
         if cache_file == None:
             session = XenAPI.xapi_local()
 
         if cache_file == None:
             session = XenAPI.xapi_local()
 
@@ -263,32 +487,70 @@ class DatabaseCache(object):
                 session._session = session_ref
 
             try:
                 session._session = session_ref
 
             try:
-                self.__vlans = session.xenapi.VLAN.get_all_records()
-                self.__bonds = session.xenapi.Bond.get_all_records()
-                self.__pifs = session.xenapi.PIF.get_all_records()
-                self.__networks = session.xenapi.network.get_all_records()
+                
+                inventory = self.__read_xensource_inventory()
+                assert(inventory.has_key('INSTALLATION_UUID'))
+                log("host uuid is %s" % inventory['INSTALLATION_UUID'])
+                
+                host = session.xenapi.host.get_by_uuid(inventory['INSTALLATION_UUID'])
+                
+                self.__get_pif_records_from_xapi(session, host)
+
+                self.__get_vlan_records_from_xapi(session)
+                self.__get_bond_records_from_xapi(session)
+                self.__get_network_records_from_xapi(session)
             finally:
                 if not session_ref:
                     session.xenapi.session.logout()
         else:
             log("Loading xapi database cache from %s" % cache_file)
             finally:
                 if not session_ref:
                     session.xenapi.session.logout()
         else:
             log("Loading xapi database cache from %s" % cache_file)
-            f = open(cache_file, 'r')
-            members = pickle.load(f)
-            self.extras = pickle.load(f)
-            f.close()
 
 
-            self.__vlans = members['vlans']
-            self.__bonds = members['bonds']
-            self.__pifs = members['pifs']
-            self.__networks = members['networks']
+            xml = parseXML(cache_file)
+
+            self.__pifs = {}
+            self.__bonds = {}
+            self.__vlans = {}
+            self.__networks = {}
 
 
-    def save(self, cache_file, extras):
+            assert(len(xml.childNodes) == 1)
+            toplevel = xml.childNodes[0]
+            
+            assert(toplevel.nodeName == "xenserver-network-configuration")
+            
+            for n in toplevel.childNodes:
+                if n.nodeName == "#text":
+                    pass
+                elif n.nodeName == PIF_XML_TAG:
+                    (ref,rec) = self.__from_xml(n, PIF_ATTRS)
+                    self.__pifs[ref] = rec
+                elif n.nodeName == BOND_XML_TAG:
+                    (ref,rec) = self.__from_xml(n, BOND_ATTRS)
+                    self.__bonds[ref] = rec
+                elif n.nodeName == VLAN_XML_TAG:
+                    (ref,rec) = self.__from_xml(n, VLAN_ATTRS)
+                    self.__vlans[ref] = rec
+                elif n.nodeName == NETWORK_XML_TAG:
+                    (ref,rec) = self.__from_xml(n, NETWORK_ATTRS)
+                    self.__networks[ref] = rec
+                else:
+                    raise Error("Unknown XML element %s" % n.nodeName)
+
+    def save(self, cache_file):
+
+        xml = getDOMImplementation().createDocument(
+            None, "xenserver-network-configuration", None)
+        for (ref,rec) in self.__pifs.items():
+            self.__to_xml(xml, xml.documentElement, PIF_XML_TAG, ref, rec, PIF_ATTRS)
+        for (ref,rec) in self.__bonds.items():
+            self.__to_xml(xml, xml.documentElement, BOND_XML_TAG, ref, rec, BOND_ATTRS)
+        for (ref,rec) in self.__vlans.items():
+            self.__to_xml(xml, xml.documentElement, VLAN_XML_TAG, ref, rec, VLAN_ATTRS)
+        for (ref,rec) in self.__networks.items():
+            self.__to_xml(xml, xml.documentElement, NETWORK_XML_TAG, ref, rec,
+                          NETWORK_ATTRS)
+            
         f = open(cache_file, 'w')
         f = open(cache_file, 'w')
-        pickle.dump({'vlans': self.__vlans,
-                     'bonds': self.__bonds,
-                     'pifs': self.__pifs,
-                     'networks': self.__networks}, f)
-        pickle.dump(extras, f)
+        f.write(xml.toprettyxml())
         f.close()
 
     def get_pif_by_uuid(self, uuid):
         f.close()
 
     def get_pif_by_uuid(self, uuid):
@@ -302,33 +564,12 @@ class DatabaseCache(object):
 
         return pifs[0]
 
 
         return pifs[0]
 
-    def get_pifs_by_record(self, record):
-        """record is partial pif record.
-        Get the pif(s) whose record matches.
-        """
-        def match(pifrec):
-            for key in record:
-                if record[key] != pifrec[key]:
-                    return False
-            return True
-            
+    def get_pifs_by_device(self, device):
         return map(lambda (ref,rec): ref,
         return map(lambda (ref,rec): ref,
-                   filter(lambda (ref,rec): match(rec),
+                   filter(lambda (ref,rec): rec['device'] == device,
                           self.__pifs.items()))
 
                           self.__pifs.items()))
 
-    def get_pif_by_record(self, record):
-        """record is partial pif record.
-        Get the pif whose record matches.
-        """
-        pifs = self.get_pifs_by_record(record)
-        if len(pifs) == 0:
-            raise Error("No matching PIF \"%s\"" % str(record))
-        elif len(pifs) > 1:
-            raise Error("Multiple matching PIFs \"%s\"" % str(record))
-
-        return pifs[0]
-
-    def get_pif_by_bridge(self, host, bridge):
+    def get_pif_by_bridge(self, bridge):
         networks = map(lambda (ref,rec): ref,
                        filter(lambda (ref,rec): rec['bridge'] == bridge,
                               self.__networks.items()))
         networks = map(lambda (ref,rec): ref,
                        filter(lambda (ref,rec): rec['bridge'] == bridge,
                               self.__networks.items()))
@@ -340,32 +581,29 @@ class DatabaseCache(object):
             nwrec = self.get_network_record(network)
             for pif in nwrec['PIFs']:
                 pifrec = self.get_pif_record(pif)
             nwrec = self.get_network_record(network)
             for pif in nwrec['PIFs']:
                 pifrec = self.get_pif_record(pif)
-                if pifrec['host'] != host:
-                    continue
                 if answer:
                 if answer:
-                    raise Error("Multiple PIFs on %s for network %s" % (host, bridge))
+                    raise Error("Multiple PIFs on host for network %s" % (bridge))
                 answer = pif
         if not answer:
                 answer = pif
         if not answer:
-            raise Error("No PIF on %s for network %s" % (host, bridge))
+            raise Error("No PIF on host for network %s" % (bridge))
         return answer
 
     def get_pif_record(self, pif):
         if self.__pifs.has_key(pif):
             return self.__pifs[pif]
         return answer
 
     def get_pif_record(self, pif):
         if self.__pifs.has_key(pif):
             return self.__pifs[pif]
-        raise Error("Unknown PIF \"%s\"" % pif)
+        raise Error("Unknown PIF \"%s\" (get_pif_record)" % pif)
     def get_all_pifs(self):
         return self.__pifs
     def pif_exists(self, pif):
         return self.__pifs.has_key(pif)
     
     def get_all_pifs(self):
         return self.__pifs
     def pif_exists(self, pif):
         return self.__pifs.has_key(pif)
     
-    def get_management_pif(self, host):
+    def get_management_pif(self):
         """ Returns the management pif on host
         """
         all = self.get_all_pifs()
         for pif in all: 
             pifrec = self.get_pif_record(pif)
         """ Returns the management pif on host
         """
         all = self.get_all_pifs()
         for pif in all: 
             pifrec = self.get_pif_record(pif)
-            if pifrec['management'] and pifrec['host'] == host :
-                return pif
+            if pifrec['management']: return pif
         return None
 
     def get_network_record(self, network):
         return None
 
     def get_network_record(self, network):
@@ -417,6 +655,7 @@ For a VLAN PIF, the datapath name is the bridge name for the PIF's VLAN slave.
 use it.)
 """
 
 use it.)
 """
 
+
     pifrec = db.get_pif_record(pif)
 
     if pifrec['VLAN'] == '-1':
     pifrec = db.get_pif_record(pif)
 
     if pifrec['VLAN'] == '-1':
@@ -433,34 +672,39 @@ The ipdev name is the same as the bridge name.
     pifrec = db.get_pif_record(pif)
     return bridge_name(pif)
 
     pifrec = db.get_pif_record(pif)
     return bridge_name(pif)
 
-def physdev_names(pif):
-    """Return the name(s) of the physical network device(s) associated with pif.
-For a VLAN PIF, the physical devices are the VLAN slave's physical devices.
-For a bond master PIF, the physical devices are the bond slaves.
-For a non-VLAN, non-bond master PIF, the physical device is the PIF itself.
+def get_physdev_pifs(pif):
+    """Return the PIFs for the physical network device(s) associated with pif.
+For a VLAN PIF, this is the VLAN slave's physical device 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.
 """
 """
-
     pifrec = db.get_pif_record(pif)
 
     if pifrec['VLAN'] != '-1':
     pifrec = db.get_pif_record(pif)
 
     if pifrec['VLAN'] != '-1':
-        return physdev_names(get_vlan_slave_of_pif(pif))
+        return get_physdev_pifs(get_vlan_slave_of_pif(pif))
     elif len(pifrec['bond_master_of']) != 0:
     elif len(pifrec['bond_master_of']) != 0:
-        physdevs = []
-        for slave in get_bond_slaves_of_pif(pif):
-            physdevs += physdev_names(slave)
-        return physdevs
+        return get_bond_slaves_of_pif(pif)
     else:
     else:
-        return [pifrec['device']]
+        return [pif]
+
+def get_physdev_names(pif):
+    """Return the name(s) of the physical network device(s) associated with pif.
+For a VLAN PIF, the physical devices are the VLAN slave's physical devices.
+For a bond master PIF, the physical devices are the bond slaves.
+For a non-VLAN, non-bond master PIF, the physical device is the PIF itself.
+"""
+
+    return [db.get_pif_record(phys)['device'] for phys in get_physdev_pifs(pif)]
 
 def log_pif_action(action, pif):
     pifrec = db.get_pif_record(pif)
 
 def log_pif_action(action, pif):
     pifrec = db.get_pif_record(pif)
-    pifrec['action'] = action
-    pifrec['interface-name'] = interface_name(pif)
-    if action == "rewrite":
-        pifrec['message'] = "Rewrite PIF %(uuid)s configuration" % pifrec
-    else:
-        pifrec['message'] = "Bring %(action)s PIF %(uuid)s" % pifrec
-    log("%(message)s: %(interface-name)s configured as %(ip_configuration_mode)s" % pifrec)
+    rec = {}
+    rec['uuid'] = pifrec['uuid']
+    rec['ip_configuration_mode'] = pifrec['ip_configuration_mode']
+    rec['action'] = action
+    rec['interface-name'] = interface_name(pif)
+    rec['message'] = "Bring %(action)s PIF %(uuid)s" % rec
+    log("%(message)s: %(interface-name)s configured as %(ip_configuration_mode)s" % rec)
 
 def get_bond_masters_of_pif(pif):
     """Returns a list of PIFs which are bond masters of this PIF"""
 
 def get_bond_masters_of_pif(pif):
     """Returns a list of PIFs which are bond masters of this PIF"""
@@ -486,7 +730,6 @@ def get_bond_slaves_of_pif(pif):
     """Returns a list of PIFs which make up the given bonded pif."""
     
     pifrec = db.get_pif_record(pif)
     """Returns a list of PIFs which make up the given bonded pif."""
     
     pifrec = db.get_pif_record(pif)
-    host = pifrec['host']
 
     bmo = pifrec['bond_master_of']
     if len(bmo) > 1:
 
     bmo = pifrec['bond_master_of']
     if len(bmo) > 1:
@@ -543,31 +786,76 @@ def run_command(command):
         return False
     return True
 
         return False
     return True
 
+def rename_netdev(old_name, new_name):
+    log("Changing the name of %s to %s" % (old_name, new_name))
+    run_command(['/sbin/ifconfig', old_name, 'down'])
+    if not run_command(['/sbin/ip', 'link', 'set', old_name,
+                        'name', new_name]):
+        raise Error("Could not rename %s to %s" % (old_name, new_name))
+
+# Check whether 'pif' exists and has the correct MAC.
+# If not, try to find a device with the correct MAC and rename it.
+# 'already_renamed' is used to avoid infinite recursion.
+def remap_pif(pif, already_renamed=[]):
+    pifrec = db.get_pif_record(pif)
+    device = pifrec['device']
+    mac = pifrec['MAC']
+
+    # Is there a network device named 'device' at all?
+    device_exists = interface_exists(device)
+    if device_exists:
+        # Yes.  Does it have MAC 'mac'?
+        found_mac = get_netdev_mac(device)
+        if found_mac and mac.lower() == found_mac.lower():
+            # Yes, everything checks out the way we want.  Nothing to do.
+            return
+    else:
+        log("No network device %s" % device)
+
+    # What device has MAC 'mac'?
+    cur_device = get_netdev_by_mac(mac)
+    if not cur_device:
+        log("No network device has MAC %s" % mac)
+        return
+
+    # First rename 'device', if it exists, to get it out of the way
+    # for 'cur_device' to replace it.
+    if device_exists:
+        rename_netdev(device, "dev%d" % random.getrandbits(24))
+
+    # Rename 'cur_device' to 'device'.
+    rename_netdev(cur_device, device)
+
+def read_first_line_of_file(name):
+    file = None
+    try:
+        file = open(name, 'r')
+        return file.readline().rstrip('\n')
+    finally:
+        if file != None:
+            file.close()
+
 def down_netdev(interface, deconfigure=True):
     if not interface_exists(interface):
         log("down_netdev: interface %s does not exist, ignoring" % interface)
         return
 def down_netdev(interface, deconfigure=True):
     if not interface_exists(interface):
         log("down_netdev: interface %s does not exist, ignoring" % interface)
         return
-    argv = ["/sbin/ifconfig", interface, 'down']
     if deconfigure:
     if deconfigure:
-        argv += ['0.0.0.0']
-
         # Kill dhclient.
         pidfile_name = '/var/run/dhclient-%s.pid' % interface
         # Kill dhclient.
         pidfile_name = '/var/run/dhclient-%s.pid' % interface
-        pidfile = None
         try:
         try:
-            pidfile = open(pidfile_name, 'r')
-            os.kill(int(pidfile.readline()), signal.SIGTERM)
+            os.kill(int(read_first_line_of_file(pidfile_name)), signal.SIGTERM)
         except:
             pass
         except:
             pass
-        if pidfile != None:
-            pidfile.close()
 
         # Remove dhclient pidfile.
         try:
             os.remove(pidfile_name)
         except:
             pass
 
         # Remove dhclient pidfile.
         try:
             os.remove(pidfile_name)
         except:
             pass
-    run_command(argv)
+        
+        run_command(["/sbin/ifconfig", interface, '0.0.0.0'])
+
+    run_command(["/sbin/ifconfig", interface, 'down'])
 
 def up_netdev(interface):
     run_command(["/sbin/ifconfig", interface, 'up'])
 
 def up_netdev(interface):
     run_command(["/sbin/ifconfig", interface, 'up'])
@@ -582,10 +870,8 @@ This is because when we are called to bring up an interface with a bond master,
 we should bring down that master."""
 
     pifrec = db.get_pif_record(pif)
 we should bring down that master."""
 
     pifrec = db.get_pif_record(pif)
-    host = pifrec['host']
 
 
-    pifs_on_host = [ __pif for __pif in db.get_all_pifs() if
-                     db.get_pif_record(__pif)['host'] == host and 
+    pifs = [ __pif for __pif in db.get_all_pifs() if
                      (not  __pif in get_bond_masters_of_pif(pif)) ]
 
     peerdns_pif = None
                      (not  __pif in get_bond_masters_of_pif(pif)) ]
 
     peerdns_pif = None
@@ -594,7 +880,7 @@ we should bring down that master."""
     # loop through all the pifs on this host looking for one with
     #   other-config:peerdns = true, and one with
     #   other-config:default-route=true
     # loop through all the pifs on this host looking for one with
     #   other-config:peerdns = true, and one with
     #   other-config:default-route=true
-    for __pif in pifs_on_host:
+    for __pif in pifs:
         __pifrec = db.get_pif_record(__pif)
         __oc = __pifrec['other_config']
         if __oc.has_key('peerdns') and __oc['peerdns'] == 'true':
         __pifrec = db.get_pif_record(__pif)
         __oc = __pifrec['other_config']
         if __oc.has_key('peerdns') and __oc['peerdns'] == 'true':
@@ -618,8 +904,8 @@ we should bring down that master."""
 
     return peerdns_pif, defaultroute_pif
 
 
     return peerdns_pif, defaultroute_pif
 
-def ethtool_settings(oc):
-    # Options for "ethtool -s"
+def run_ethtool(device, oc):
+    # Run "ethtool -s" if there are any settings.
     settings = []
     if oc.has_key('ethtool-speed'):
         val = oc['ethtool-speed']
     settings = []
     if oc.has_key('ethtool-speed'):
         val = oc['ethtool-speed']
@@ -641,8 +927,10 @@ def ethtool_settings(oc):
             settings += ['autoneg', 'off']
         else:
             log("Invalid value for ethtool-autoneg = %s. Must be on|true|off|false." % val)
             settings += ['autoneg', 'off']
         else:
             log("Invalid value for ethtool-autoneg = %s. Must be on|true|off|false." % val)
+    if settings:
+        run_command(['/sbin/ethtool', '-s', device] + settings)
 
 
-    # Options for "ethtool -K"
+    # Run "ethtool -K" if there are any offload settings.
     offload = []
     for opt in ("rx", "tx", "sg", "tso", "ufo", "gso"):
         if oc.has_key("ethtool-" + opt):
     offload = []
     for opt in ("rx", "tx", "sg", "tso", "ufo", "gso"):
         if oc.has_key("ethtool-" + opt):
@@ -653,18 +941,30 @@ def ethtool_settings(oc):
                 offload += [opt, 'off']
             else:
                 log("Invalid value for ethtool-%s = %s. Must be on|true|off|false." % (opt, val))
                 offload += [opt, 'off']
             else:
                 log("Invalid value for ethtool-%s = %s. Must be on|true|off|false." % (opt, val))
+    if offload:
+        run_command(['/sbin/ethtool', '-K', device] + offload)
 
 
-    return settings, offload
+def mtu_setting(oc):
+    if oc.has_key('mtu'):
+        try:
+            int(oc['mtu'])      # Check that the value is an integer
+            return ['mtu', oc['mtu']]
+        except ValueError, x:
+            log("Invalid value for mtu = %s" % mtu)
+    return []
 
 
-def configure_netdev(pif):
+def configure_local_port(pif):
     pifrec = db.get_pif_record(pif)
     datapath = datapath_name(pif)
     ipdev = ipdev_name(pif)
 
     pifrec = db.get_pif_record(pif)
     datapath = datapath_name(pif)
     ipdev = ipdev_name(pif)
 
-    host = pifrec['host']
     nw = pifrec['network']
     nwrec = db.get_network_record(nw)
 
     nw = pifrec['network']
     nwrec = db.get_network_record(nw)
 
+    pif_oc = pifrec['other_config']
+    nw_oc = nwrec['other_config']
+
+    # IP (except DHCP) and MTU.
     ifconfig_argv = ['/sbin/ifconfig', ipdev, 'up']
     gateway = ''
     if pifrec['ip_configuration_mode'] == "DHCP":
     ifconfig_argv = ['/sbin/ifconfig', ipdev, 'up']
     gateway = ''
     if pifrec['ip_configuration_mode'] == "DHCP":
@@ -678,45 +978,37 @@ def configure_netdev(pif):
         pass
     else:
         raise Error("Unknown IP-configuration-mode %s" % pifrec['ip_configuration_mode'])
         pass
     else:
         raise Error("Unknown IP-configuration-mode %s" % pifrec['ip_configuration_mode'])
-
-    oc = {}
-    if pifrec.has_key('other_config'):
-        oc = pifrec['other_config']
-        if oc.has_key('mtu'):
-            int(oc['mtu'])      # Check that the value is an integer
-            ifconfig_argv += ['mtu', oc['mtu']]
-
+    ifconfig_argv += mtu_setting(nw_oc)
     run_command(ifconfig_argv)
     
     (peerdns_pif, defaultroute_pif) = find_distinguished_pifs(pif)
 
     run_command(ifconfig_argv)
     
     (peerdns_pif, defaultroute_pif) = find_distinguished_pifs(pif)
 
+    # /etc/resolv.conf
     if peerdns_pif == pif:
         f = ConfigurationFile('resolv.conf', "/etc")
     if peerdns_pif == pif:
         f = ConfigurationFile('resolv.conf', "/etc")
-        if oc.has_key('domain'):
-            f.write("search %s\n" % oc['domain'])
+        if pif_oc.has_key('domain'):
+            f.write("search %s\n" % pif_oc['domain'])
         for dns in pifrec['DNS'].split(","): 
             f.write("nameserver %s\n" % dns)
         f.close()
         f.apply()
         f.commit()
 
         for dns in pifrec['DNS'].split(","): 
             f.write("nameserver %s\n" % dns)
         f.close()
         f.apply()
         f.commit()
 
+    # Routing.
     if defaultroute_pif == pif and gateway != '':
         run_command(['/sbin/ip', 'route', 'replace', 'default',
                      'via', gateway, 'dev', ipdev])
     if defaultroute_pif == pif and gateway != '':
         run_command(['/sbin/ip', 'route', 'replace', 'default',
                      'via', gateway, 'dev', ipdev])
-    
-    if oc.has_key('static-routes'):
-        for line in oc['static-routes'].split(','):
+    if nw_oc.has_key('static-routes'):
+        for line in nw_oc['static-routes'].split(','):
             network, masklen, gateway = line.split('/')
             run_command(['/sbin/ip', 'route', 'add',
             network, masklen, gateway = line.split('/')
             run_command(['/sbin/ip', 'route', 'add',
-                         '%s/%s' % (netmask, masklen), 'via', gateway,
+                         '%s/%s' % (network, masklen), 'via', gateway,
                          'dev', ipdev])
 
                          'dev', ipdev])
 
-    settings, offload = ethtool_settings(oc)
-    if settings:
-        run_command(['/sbin/ethtool', '-s', ipdev] + settings)
-    if offload:
-        run_command(['/sbin/ethtool', '-K', ipdev] + offload)
+    # Ethtool.
+    run_ethtool(ipdev, nw_oc)
 
 
+    # DHCP.
     if pifrec['ip_configuration_mode'] == "DHCP":
         print
         print "Determining IP information for %s..." % ipdev,
     if pifrec['ip_configuration_mode'] == "DHCP":
         print
         print "Determining IP information for %s..." % ipdev,
@@ -729,8 +1021,16 @@ def configure_netdev(pif):
         else:
             print 'failed.'
 
         else:
             print 'failed.'
 
+def configure_physdev(pif):
+    pifrec = db.get_pif_record(pif)
+    device = pifrec['device']
+    oc = pifrec['other_config']
+
+    run_command(['/sbin/ifconfig', device, 'up'] + mtu_setting(oc))
+    run_ethtool(device, oc)
+
 def modify_config(commands):
 def modify_config(commands):
-    run_command(['/root/vswitch/bin/ovs-cfg-mod', '-vANY:console:emer',
+    run_command(['/usr/bin/ovs-cfg-mod', '-vANY:console:emer',
                  '-F', '/etc/ovs-vswitchd.conf']
                 + commands + ['-c'])
     run_command(['/sbin/service', 'vswitch', 'reload'])
                  '-F', '/etc/ovs-vswitchd.conf']
                 + commands + ['-c'])
     run_command(['/sbin/service', 'vswitch', 'reload'])
@@ -744,11 +1044,15 @@ def configure_bond(pif):
     interface = interface_name(pif)
     ipdev = ipdev_name(pif)
     datapath = datapath_name(pif)
     interface = interface_name(pif)
     ipdev = ipdev_name(pif)
     datapath = datapath_name(pif)
-    physdevs = physdev_names(pif)
+    physdev_names = get_physdev_names(pif)
 
     argv = ['--del-match=bonding.%s.[!0-9]*' % interface]
     argv += ["--add=bonding.%s.slave=%s" % (interface, slave)
 
     argv = ['--del-match=bonding.%s.[!0-9]*' % interface]
     argv += ["--add=bonding.%s.slave=%s" % (interface, slave)
-             for slave in physdevs]
+             for slave in physdev_names]
+    argv += ['--add=bonding.%s.fake-iface=true' % interface]
+
+    if pifrec['MAC'] != "":
+        argv += ['--add=port.%s.mac=%s' % (interface, pifrec['MAC'])]
 
     # Bonding options.
     bond_options = { 
 
     # Bonding options.
     bond_options = { 
@@ -776,7 +1080,8 @@ def action_up(pif):
     interface = interface_name(pif)
     ipdev = ipdev_name(pif)
     datapath = datapath_name(pif)
     interface = interface_name(pif)
     ipdev = ipdev_name(pif)
     datapath = datapath_name(pif)
-    physdevs = physdev_names(pif)
+    physdev_pifs = get_physdev_pifs(pif)
+    physdev_names = get_physdev_names(pif)
     vlan_slave = None
     if pifrec['VLAN'] != '-1':
         vlan_slave = get_vlan_slave_of_pif(pif)
     vlan_slave = None
     if pifrec['VLAN'] != '-1':
         vlan_slave = get_vlan_slave_of_pif(pif)
@@ -818,9 +1123,9 @@ def action_up(pif):
 
     # /etc/xensource/scripts/vif needs to know where to add VIFs.
     if vlan_slave:
 
     # /etc/xensource/scripts/vif needs to know where to add VIFs.
     if vlan_slave:
-        if not os.path.exists(vswitch_config_dir):
-            os.mkdir(vswitch_config_dir)
-        br = ConfigurationFile("br-%s" % bridge, vswitch_config_dir)
+        if not os.path.exists(vswitch_state_dir):
+            os.mkdir(vswitch_state_dir)
+        br = ConfigurationFile("br-%s" % bridge, vswitch_state_dir)
         br.write("VLAN_SLAVE=%s\n" % datapath)
         br.write("VLAN_VID=%s\n" % pifrec['VLAN'])
         br.close()
         br.write("VLAN_SLAVE=%s\n" % datapath)
         br.write("VLAN_VID=%s\n" % pifrec['VLAN'])
         br.close()
@@ -830,10 +1135,15 @@ def action_up(pif):
     f.apply()
     f.commit()
 
     f.apply()
     f.commit()
 
+    # Check the MAC address of each network device and remap if
+    # necessary to make names match our expectations.
+    for physdev_pif in physdev_pifs:
+        remap_pif(physdev_pif)
+
     # "ifconfig down" the network device and delete its IP address, etc.
     down_netdev(ipdev)
     # "ifconfig down" the network device and delete its IP address, etc.
     down_netdev(ipdev)
-    for physdev in physdevs:
-        down_netdev(physdev)
+    for physdev_name in physdev_names:
+        down_netdev(physdev_name)
 
     # If we are bringing up a bond, remove IP addresses from the
     # slaves (because we are implicitly being asked to take them down).
 
     # If we are bringing up a bond, remove IP addresses from the
     # slaves (because we are implicitly being asked to take them down).
@@ -845,7 +1155,7 @@ def action_up(pif):
         run_command(["/sbin/ifconfig", ipdev_name(bond_pif), '0.0.0.0']) 
 
     # Remove all keys related to pif and any bond masters linked to PIF.
         run_command(["/sbin/ifconfig", ipdev_name(bond_pif), '0.0.0.0']) 
 
     # Remove all keys related to pif and any bond masters linked to PIF.
-    del_ports = [ipdev] + physdevs + bond_masters
+    del_ports = [ipdev] + physdev_names + bond_masters
     if vlan_slave and bond_master:
         del_ports += [interface_name(bond_master)]
     
     if vlan_slave and bond_master:
         del_ports += [interface_name(bond_master)]
     
@@ -856,7 +1166,7 @@ def action_up(pif):
     # port.
     add_ports = [ipdev, datapath]
     if not bond_master:
     # port.
     add_ports = [ipdev, datapath]
     if not bond_master:
-        add_ports += physdevs
+        add_ports += physdev_names
     else:
         add_ports += [interface_name(bond_master)]
 
     else:
         add_ports += [interface_name(bond_master)]
 
@@ -871,7 +1181,7 @@ def action_up(pif):
     #  - The bond masters for pif.  (Ordinarily pif shouldn't have any
     #    bond masters.  If it does then interface-reconfigure is
     #    implicitly being asked to take them down.)
     #  - The bond masters for pif.  (Ordinarily pif shouldn't have any
     #    bond masters.  If it does then interface-reconfigure is
     #    implicitly being asked to take them down.)
-    del_ports = add_ports + physdevs + bond_masters
+    del_ports = add_ports + physdev_names + bond_masters
 
     # What networks does this datapath carry?
     #
 
     # What networks does this datapath carry?
     #
@@ -880,8 +1190,7 @@ def action_up(pif):
     # - The networks corresponding to any VLANs attached to the
     #   datapath's PIF.
     network_uuids = []
     # - The networks corresponding to any VLANs attached to the
     #   datapath's PIF.
     network_uuids = []
-    for nwpif in db.get_pifs_by_record({'device': pifrec['device'],
-                                        'host': pifrec['host']}):
+    for nwpif in db.get_pifs_by_device(pifrec['device']):
         net = db.get_pif_record(nwpif)['network']
         network_uuids += [db.get_network_record(net)['uuid']]
 
         net = db.get_pif_record(nwpif)['network']
         network_uuids += [db.get_network_record(net)['uuid']]
 
@@ -889,11 +1198,11 @@ def action_up(pif):
     # 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".
     # 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".
-    bond_slave_physdevs = []
+    bond_slave_physdev_pifs = []
     for slave in bond_slaves:
     for slave in bond_slaves:
-        bond_slave_physdevs += physdev_names(slave)
-    for slave_physdev in bond_slave_physdevs:
-        up_netdev(slave_physdev)
+        bond_slave_physdev_pifs += get_physdev_pifs(slave)
+    for slave_physdev_pif in set(bond_slave_physdev_pifs):
+        configure_physdev(slave_physdev_pif)
 
     # Now modify the ovs-vswitchd config file.
     argv = []
 
     # Now modify the ovs-vswitchd config file.
     argv = []
@@ -916,7 +1225,7 @@ def action_up(pif):
         argv += ['--add=iface.%s.fake-bridge=true' % (ipdev)]
     else:
         try:
         argv += ['--add=iface.%s.fake-bridge=true' % (ipdev)]
     else:
         try:
-            os.unlink("%s/br-%s" % (vswitch_config_dir, bridge))
+            os.unlink("%s/br-%s" % (vswitch_state_dir, bridge))
         except OSError:
             pass
     argv += ['--del-match=bridge.%s.xs-network-uuids=*' % datapath]
         except OSError:
             pass
     argv += ['--del-match=bridge.%s.xs-network-uuids=*' % datapath]
@@ -926,15 +1235,17 @@ def action_up(pif):
         argv += configure_bond(bond_master)
     modify_config(argv)
 
         argv += configure_bond(bond_master)
     modify_config(argv)
 
-    # Configure network devices.
-    configure_netdev(pif)
-
     # Bring up VLAN slave, plus physical devices other than bond
     # slaves (which we brought up earlier).
     # Bring up VLAN slave, plus physical devices other than bond
     # slaves (which we brought up earlier).
+    # XXX need to bring up bond itself.
+    # XXX need to set MAC address on fake bridge
     if vlan_slave:
         up_netdev(ipdev_name(vlan_slave))
     if vlan_slave:
         up_netdev(ipdev_name(vlan_slave))
-    for physdev in set(physdevs) - set(bond_slave_physdevs):
-        up_netdev(physdev)
+    for physdev_pif in set(physdev_pifs) - set(bond_slave_physdev_pifs):
+        configure_physdev(physdev_pif)
+
+    # Configure network device for local port.
+    configure_local_port(pif)
 
     # Update /etc/issue (which contains the IP address of the management interface)
     os.system("/sbin/update-issue")
 
     # Update /etc/issue (which contains the IP address of the management interface)
     os.system("/sbin/update-issue")
@@ -1004,6 +1315,7 @@ def action_down(pif):
         log("VLAN masters of %s - %s" % (rec['device'], [interface_name(m) for m in vlan_masters]))
         for m in vlan_masters:
             if db.get_pif_record(m)['currently_attached']:
         log("VLAN masters of %s - %s" % (rec['device'], [interface_name(m) for m in vlan_masters]))
         for m in vlan_masters:
             if db.get_pif_record(m)['currently_attached']:
+                # XXX remove IP address
                 log("Leaving %s up due to currently attached VLAN master %s" % (interface, interface_name(m)))
                 return
 
                 log("Leaving %s up due to currently attached VLAN master %s" % (interface, interface_name(m)))
                 return
 
@@ -1059,6 +1371,9 @@ def action_rewrite(pif):
     # We have no code of our own to run here.
     pass
 
     # We have no code of our own to run here.
     pass
 
+def action_force_rewrite(interface, config):
+    raise Error("Force rewrite is not implemented yet.")
+
 def main(argv=None):
     global output_directory, management_pif
     
 def main(argv=None):
     global output_directory, management_pif
     
@@ -1081,7 +1396,6 @@ def main(argv=None):
                         "force=",
                         "force-interface=",
                         "management",
                         "force=",
                         "force-interface=",
                         "management",
-                        "test-mode",
                         "device=", "mode=", "ip=", "netmask=", "gateway=",
                         "help" ]
             arglist, args = getopt.gnu_getopt(argv[1:], shortops, longops)
                         "device=", "mode=", "ip=", "netmask=", "gateway=",
                         "help" ]
             arglist, args = getopt.gnu_getopt(argv[1:], shortops, longops)
@@ -1118,6 +1432,10 @@ def main(argv=None):
             raise Usage("Too many arguments")
 
         action = args[0]
             raise Usage("Too many arguments")
 
         action = args[0]
+
+        if not action in ["up", "down", "rewrite", "rewrite-configuration"]:
+            raise Usage("Unknown action \"%s\"" % action)
+
         # backwards compatibility
         if action == "rewrite-configuration": action = "rewrite"
         
         # backwards compatibility
         if action == "rewrite-configuration": action = "rewrite"
         
@@ -1129,6 +1447,8 @@ def main(argv=None):
             raise Usage("--session and --pif must be used together.")
         if force_interface and ( session or pif or pif_uuid ):
             raise Usage("--force is mutually exclusive with --session, --pif and --pif-uuid")
             raise Usage("--session and --pif must be used together.")
         if force_interface and ( session or pif or pif_uuid ):
             raise Usage("--force is mutually exclusive with --session, --pif and --pif-uuid")
+        if force_interface == "all" and action != "down":
+            raise Usage("\"--force all\" only valid for down action")
         if len(force_rewrite_config) and not (force_interface and action == "rewrite"):
             raise Usage("\"--force rewrite\" needed for --device, --mode, --ip, --netmask, and --gateway")
 
         if len(force_rewrite_config) and not (force_interface and action == "rewrite"):
             raise Usage("\"--force rewrite\" needed for --device, --mode, --ip, --netmask, and --gateway")
 
@@ -1138,54 +1458,57 @@ def main(argv=None):
 
             if action == "rewrite":
                 action_force_rewrite(force_interface, force_rewrite_config)
 
             if action == "rewrite":
                 action_force_rewrite(force_interface, force_rewrite_config)
-            else:
+            elif action in ["up", "down"]:
+                if action == "down" and force_interface == "all":
+                    raise Error("Force all interfaces down not implemented yet")
+
                 db = DatabaseCache(cache_file=dbcache_file)
                 db = DatabaseCache(cache_file=dbcache_file)
-                host = db.extras['host']
-                pif = db.get_pif_by_bridge(host, force_interface)
-                management_pif = db.get_management_pif(host)
+                pif = db.get_pif_by_bridge(force_interface)
+                management_pif = db.get_management_pif()
 
                 if action == "up":
                     action_up(pif)
                 elif action == "down":
                     action_down(pif)
 
                 if action == "up":
                     action_up(pif)
                 elif action == "down":
                     action_down(pif)
-                else:
-                    raise Usage("Unknown action %s"  % action)
+            else:
+                raise Error("Unknown action %s"  % action)
         else:
             db = DatabaseCache(session_ref=session)
 
             if pif_uuid:
                 pif = db.get_pif_by_uuid(pif_uuid)
         else:
             db = DatabaseCache(session_ref=session)
 
             if pif_uuid:
                 pif = db.get_pif_by_uuid(pif_uuid)
-        
-            if not pif:
-                raise Usage("No PIF given")
 
 
-            if force_management:
-                # pif is going to be the management pif 
-                management_pif = pif
+            if action == "rewrite" and not pif:
+                pass
             else:
             else:
-                # pif is not going to be the management pif.
-                # Search DB cache for pif on same host with management=true
-                pifrec = db.get_pif_record(pif)
-                host = pifrec['host']
-                management_pif = db.get_management_pif(host)
+                if not pif:
+                    raise Usage("No PIF given")
 
 
-            log_pif_action(action, pif)
+                if force_management:
+                    # pif is going to be the management pif 
+                    management_pif = pif
+                else:
+                    # pif is not going to be the management pif.
+                    # Search DB cache for pif on same host with management=true
+                    pifrec = db.get_pif_record(pif)
+                    management_pif = db.get_management_pif()
 
 
-            if not check_allowed(pif):
-                return 0
+                log_pif_action(action, pif)
 
 
-            if action == "up":
-                action_up(pif)
-            elif action == "down":
-                action_down(pif)
-            elif action == "rewrite":
-                action_rewrite(pif)
-            else:
-                raise Usage("Unknown action %s"  % action)
+                if not check_allowed(pif):
+                    return 0
+
+                if action == "up":
+                    action_up(pif)
+                elif action == "down":
+                    action_down(pif)
+                elif action == "rewrite":
+                    action_rewrite(pif)
+                else:
+                    raise Error("Unknown action %s"  % action)
 
             # Save cache.
 
             # Save cache.
-            pifrec = db.get_pif_record(pif)
-            db.save(dbcache_file, {'host': pifrec['host']})
+            db.save(dbcache_file)
         
     except Usage, err:
         print >>sys.stderr, err.msg
         
     except Usage, err:
         print >>sys.stderr, err.msg
@@ -1351,7 +1674,6 @@ def configure_network(pif, f):
     """
     
     pifrec = db.get_pif_record(pif)
     """
     
     pifrec = db.get_pif_record(pif)
-    host = pifrec['host']
     nw = pifrec['network']
     nwrec = db.get_network_record(nw)
     oc = None
     nw = pifrec['network']
     nwrec = db.get_network_record(nw)
     oc = None
@@ -1406,8 +1728,7 @@ def configure_network(pif, f):
     # This is because when we are called to bring up an interface with a bond master, it is implicit that
     # we should bring down that master.
     pifs_on_host = [ __pif for __pif in db.get_all_pifs() if
     # This is because when we are called to bring up an interface with a bond master, it is implicit that
     # we should bring down that master.
     pifs_on_host = [ __pif for __pif in db.get_all_pifs() if
-                     db.get_pif_record(__pif)['host'] == host and 
-                     (not  __pif in get_bond_masters_of_pif(pif)) ]
+                     not __pif in get_bond_masters_of_pif(pif) ]
     other_pifs_on_host = [ __pif for __pif in pifs_on_host if __pif != pif ]
 
     peerdns_pif = None
     other_pifs_on_host = [ __pif for __pif in pifs_on_host if __pif != pif ]
 
     peerdns_pif = None