xenserver: Drop "init-dbcache" by making PIF optional for "rewrite".
[openvswitch] / xenserver / opt_xensource_libexec_interface-reconfigure
index 6ea369ffbbc3017d743cc839627abf3c1d9aee9b..27195ad93e69c60f3f59316edcf0de53cd77711f 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/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)
@@ -786,6 +1091,10 @@ def action_up(pif):
         bond_master = pif
     else:
         bond_master = None
         bond_master = pif
     else:
         bond_master = None
+    if bond_master:
+        bond_slaves = get_bond_slaves_of_pif(bond_master)
+    else:
+        bond_slaves = []
     bond_masters = get_bond_masters_of_pif(pif)
 
     # Support "rpm -e vswitch" gracefully by keeping Centos configuration
     bond_masters = get_bond_masters_of_pif(pif)
 
     # Support "rpm -e vswitch" gracefully by keeping Centos configuration
@@ -814,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()
@@ -826,13 +1135,27 @@ 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).
+    # 
+    # Conversely, if we are bringing up an interface that has bond
+    # masters, remove IP addresses from the bond master (because we
+    # are implicitly being asked to take it down).
+    for bond_pif in bond_slaves + bond_masters:
+        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.
 
     # 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)]
     
@@ -843,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)]
 
@@ -858,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?
     #
@@ -867,11 +1190,20 @@ 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']]
 
+    # Bring up bond slaves 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".
+    bond_slave_physdev_pifs = []
+    for slave in bond_slaves:
+        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 = []
     for port in set(del_ports):
     # Now modify the ovs-vswitchd config file.
     argv = []
     for port in set(del_ports):
@@ -893,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]
@@ -903,17 +1235,31 @@ 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 and bond slaves.
+    # Bring up VLAN slave, plus physical devices other than bond
+    # slaves (which we brought up earlier).
     if vlan_slave:
         up_netdev(ipdev_name(vlan_slave))
     if vlan_slave:
         up_netdev(ipdev_name(vlan_slave))
-    for physdev in 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")
+
+    if bond_slaves:
+        # There seems to be a race somewhere: without this sleep, using
+        # XenCenter to create a bond that becomes the management interface
+        # fails with "The underlying connection was closed: A connection that
+        # was expected to be kept alive was closed by the server." on every
+        # second or third try, even though /var/log/messages doesn't show
+        # anything unusual.
+        #
+        # The race is probably present even without vswitch, but bringing up a
+        # bond without vswitch involves a built-in pause of 10 seconds or more
+        # to wait for the bond to transition from learning to forwarding state.
+        time.sleep(5)
         
 def action_down(pif):
     rec = db.get_pif_record(pif)    
         
 def action_down(pif):
     rec = db.get_pif_record(pif)    
@@ -1103,9 +1449,8 @@ def main(argv=None):
                 action_force_rewrite(force_interface, force_rewrite_config)
             else:
                 db = DatabaseCache(cache_file=dbcache_file)
                 action_force_rewrite(force_interface, force_rewrite_config)
             else:
                 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)
 
                 if action == "up":
                     action_up(pif)
@@ -1118,37 +1463,38 @@ def main(argv=None):
 
             if pif_uuid:
                 pif = db.get_pif_by_uuid(pif_uuid)
 
             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 Usage("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
@@ -1314,7 +1660,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
@@ -1369,8 +1714,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