-#!/usr/bin/python
+#!/usr/bin/env python
#
# Copyright (c) 2008,2009 Citrix Systems, Inc.
-# Copyright (c) 2009 Nicira Networks.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
%(command-name)s <PIF> up
%(command-name)s <PIF> down
- %(command-name)s [<PIF>] rewrite
+ %(command-name)s 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 <BRIDGE> rewrite --device=<INTERFACE> --mac=<MAC-ADDRESS> <CONFIG>
where <PIF> is one of:
--session <SESSION-REF> --pif <PIF-REF>
--mode=static --ip=<IPADDR> --netmask=<NM> [--gateway=<GW>]
Options:
- --session A session reference to use to access the xapi DB
+ --session A session reference to use to access the xapi DB
--pif A PIF reference within the session.
--pif-uuid The UUID of a PIF.
--force An interface name.
+ --root-prefix=DIR Use DIR as alternate root directory (for testing).
+ --no-syslog Write log messages to stderr instead of system log.
"""
-#
-# Undocumented parameters for test & dev:
-#
-# --output-directory=<DIR> Write configuration to <DIR>. Also disables actually
-# raising/lowering the interfaces
-#
-#
-#
# Notes:
# 1. Every pif belongs to exactly one network
# 2. Every network has zero or one pifs
# 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)
-import XenAPI
-import os, sys, getopt, time, signal
+from InterfaceReconfigure import *
+
+import os, sys, getopt
import syslog
import traceback
import re
import random
-from xml.dom.minidom import getDOMImplementation
-from xml.dom.minidom import parse as parseXML
-
-output_directory = None
+import syslog
-db = None
management_pif = None
-vswitch_state_dir = "/var/lib/openvswitch/"
-dbcache_file = vswitch_state_dir + "dbcache"
+dbcache_file = "/var/xapi/network.dbcache"
#
-# Debugging and Logging.
+# Logging.
#
-def debug_mode():
- return output_directory is not None
-
-def log(s):
- if debug_mode():
- print >>sys.stderr, s
- else:
- syslog.syslog(s)
-
def log_pif_action(action, pif):
- pifrec = db.get_pif_record(pif)
+ pifrec = db().get_pif_record(pif)
rec = {}
rec['uuid'] = pifrec['uuid']
rec['ip_configuration_mode'] = pifrec['ip_configuration_mode']
rec['message'] = "Bring %(action)s PIF %(uuid)s" % rec
log("%(message)s: %(pif_netdev_name)s configured as %(ip_configuration_mode)s" % rec)
-
-def run_command(command):
- log("Running command: " + ' '.join(command))
- rc = os.spawnl(os.P_WAIT, command[0], *command)
- if rc != 0:
- log("Command failed %d: " % rc + ' '.join(command))
- return False
- return True
-
#
# Exceptions.
#
Exception.__init__(self)
self.msg = msg
-class Error(Exception):
- def __init__(self, msg):
- Exception.__init__(self)
- self.msg = msg
-
-#
-# Configuration File Handling.
-#
-
-class ConfigurationFile(object):
- """Write a file, tracking old and new versions.
-
- Supports writing a new version of a file and applying and
- reverting those changes.
- """
-
- __STATE = {"OPEN":"OPEN",
- "NOT-APPLIED":"NOT-APPLIED", "APPLIED":"APPLIED",
- "REVERTED":"REVERTED", "COMMITTED": "COMMITTED"}
-
- def __init__(self, fname, path="/etc/sysconfig/network-scripts"):
-
- self.__state = self.__STATE['OPEN']
- self.__fname = fname
- self.__children = []
-
- if debug_mode():
- dirname = output_directory
- else:
- dirname = path
-
- self.__path = os.path.join(dirname, fname)
- self.__oldpath = os.path.join(dirname, "." + fname + ".xapi-old")
- self.__newpath = os.path.join(dirname, "." + fname + ".xapi-new")
- self.__unlink = False
-
- self.__f = open(self.__newpath, "w")
-
- def attach_child(self, child):
- self.__children.append(child)
-
- def path(self):
- return self.__path
-
- def readlines(self):
- try:
- return open(self.path()).readlines()
- except:
- return ""
-
- def write(self, args):
- if self.__state != self.__STATE['OPEN']:
- raise Error("Attempt to write to file in state %s" % self.__state)
- self.__f.write(args)
-
- def unlink(self):
- if self.__state != self.__STATE['OPEN']:
- raise Error("Attempt to unlink file in state %s" % self.__state)
- self.__unlink = True
- self.__f.close()
- self.__state = self.__STATE['NOT-APPLIED']
-
- def close(self):
- if self.__state != self.__STATE['OPEN']:
- raise Error("Attempt to close file in state %s" % self.__state)
-
- self.__f.close()
- self.__state = self.__STATE['NOT-APPLIED']
-
- def changed(self):
- if self.__state != self.__STATE['NOT-APPLIED']:
- raise Error("Attempt to compare file in state %s" % self.__state)
-
- return True
-
- def apply(self):
- if self.__state != self.__STATE['NOT-APPLIED']:
- raise Error("Attempt to apply configuration from state %s" % self.__state)
-
- for child in self.__children:
- child.apply()
-
- log("Applying changes to %s configuration" % self.__fname)
-
- # Remove previous backup.
- if os.access(self.__oldpath, os.F_OK):
- os.unlink(self.__oldpath)
-
- # Save current configuration.
- if os.access(self.__path, os.F_OK):
- os.link(self.__path, self.__oldpath)
- os.unlink(self.__path)
-
- # Apply new configuration.
- assert(os.path.exists(self.__newpath))
- if not self.__unlink:
- os.link(self.__newpath, self.__path)
- else:
- pass # implicit unlink of original file
-
- # Remove temporary file.
- os.unlink(self.__newpath)
-
- self.__state = self.__STATE['APPLIED']
-
- def revert(self):
- if self.__state != self.__STATE['APPLIED']:
- raise Error("Attempt to revert configuration from state %s" % self.__state)
-
- for child in self.__children:
- child.revert()
-
- log("Reverting changes to %s configuration" % self.__fname)
-
- # Remove existing new configuration
- if os.access(self.__newpath, os.F_OK):
- os.unlink(self.__newpath)
-
- # Revert new configuration.
- if os.access(self.__path, os.F_OK):
- os.link(self.__path, self.__newpath)
- os.unlink(self.__path)
-
- # Revert to old configuration.
- if os.access(self.__oldpath, os.F_OK):
- os.link(self.__oldpath, self.__path)
- os.unlink(self.__oldpath)
-
- # Leave .*.xapi-new as an aid to debugging.
-
- self.__state = self.__STATE['REVERTED']
-
- def commit(self):
- if self.__state != self.__STATE['APPLIED']:
- raise Error("Attempt to commit configuration from state %s" % self.__state)
-
- for child in self.__children:
- child.commit()
-
- log("Committing changes to %s configuration" % self.__fname)
-
- if os.access(self.__oldpath, os.F_OK):
- os.unlink(self.__oldpath)
- if os.access(self.__newpath, os.F_OK):
- os.unlink(self.__newpath)
-
- self.__state = self.__STATE['COMMITTED']
-
-#
-# Helper functions for encoding/decoding database attributes to/from XML.
-#
-
-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_OTHERCONFIG_ATTRS = [ 'domain', 'peerdns', 'defaultroute', 'mtu', 'static-routes' ] + \
- [ 'bond-%s' % x for x in 'mode', 'miimon', 'downdelay', 'updelay', 'use_carrier' ] + \
- ETHTOOL_OTHERCONFIG_ATTRS
-
-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),
- }
-
-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_OTHERCONFIG_ATTRS = [ 'mtu', 'static-routes' ] + ETHTOOL_OTHERCONFIG_ATTRS
-
-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)),
- }
-
-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")
- if cache_file == None:
- session = XenAPI.xapi_local()
-
- if not session_ref:
- log("No session ref given on command line, logging in.")
- session.xenapi.login_with_password("root", "")
- else:
- session._session = session_ref
-
- try:
-
- 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)
-
- xml = parseXML(cache_file)
-
- self.__pifs = {}
- self.__bonds = {}
- self.__vlans = {}
- self.__networks = {}
-
- 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.write(xml.toprettyxml())
- f.close()
-
- def get_pif_by_uuid(self, uuid):
- pifs = map(lambda (ref,rec): ref,
- filter(lambda (ref,rec): uuid == rec['uuid'],
- self.__pifs.items()))
- if len(pifs) == 0:
- raise Error("Unknown PIF \"%s\"" % uuid)
- elif len(pifs) > 1:
- raise Error("Non-unique PIF \"%s\"" % uuid)
-
- return pifs[0]
-
- def get_pifs_by_device(self, device):
- return map(lambda (ref,rec): ref,
- filter(lambda (ref,rec): rec['device'] == device,
- self.__pifs.items()))
-
- def get_pif_by_bridge(self, bridge):
- networks = map(lambda (ref,rec): ref,
- filter(lambda (ref,rec): rec['bridge'] == bridge,
- self.__networks.items()))
- if len(networks) == 0:
- raise Error("No matching network \"%s\"" % bridge)
-
- answer = None
- for network in networks:
- nwrec = self.get_network_record(network)
- for pif in nwrec['PIFs']:
- pifrec = self.get_pif_record(pif)
- if answer:
- raise Error("Multiple PIFs on host for network %s" % (bridge))
- answer = pif
- if not answer:
- 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]
- 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_management_pif(self):
- """ Returns the management pif on host
- """
- all = self.get_all_pifs()
- for pif in all:
- pifrec = self.get_pif_record(pif)
- if pifrec['management']: return pif
- return None
-
- def get_network_record(self, network):
- if self.__networks.has_key(network):
- return self.__networks[network]
- raise Error("Unknown network \"%s\"" % network)
- def get_all_networks(self):
- return self.__networks
-
- def get_bond_record(self, bond):
- if self.__bonds.has_key(bond):
- return self.__bonds[bond]
- else:
- return None
-
- def get_vlan_record(self, vlan):
- if self.__vlans.has_key(vlan):
- return self.__vlans[vlan]
- else:
- return None
-
#
# Boot from Network filesystem or device.
#
Used to prevent system PIFs (such as network root disk) from being interfered with.
"""
- pifrec = db.get_pif_record(pif)
+ pifrec = db().get_pif_record(pif)
try:
- f = open("/proc/ardence")
+ f = open(root_prefix() + "/proc/ardence")
macline = filter(lambda x: x.startswith("HWaddr:"), f.readlines())
f.close()
if len(macline) == 1:
# Bare Network Devices -- network devices without IP configuration
#
-def netdev_exists(netdev):
- return os.path.exists("/sys/class/net/" + netdev)
-
-def pif_netdev_name(pif):
- """Get the netdev name for a PIF."""
-
- pifrec = db.get_pif_record(pif)
-
- if pif_is_vlan(pif):
- return "%(device)s.%(VLAN)s" % pifrec
- else:
- return pifrec['device']
-
-def netdev_down(netdev):
- """Bring down a bare network device"""
- if debug_mode():
- return
- if not netdev_exists(netdev):
- log("netdev: down: device %s does not exist, ignoring" % netdev)
- return
- run_command(["/sbin/ifconfig", netdev, 'down'])
-
-def netdev_up(netdev, mtu=None):
- """Bring up a bare network device"""
- if debug_mode():
- return
- if not netdev_exists(netdev):
- raise Error("netdev: up: device %s does not exist" % netdev)
-
- if mtu:
- mtu = ["mtu", mtu]
- else:
- mtu = []
-
- run_command(["/sbin/ifconfig", netdev, 'up'] + mtu)
-
def netdev_remap_name(pif, already_renamed=[]):
"""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 read1(name):
file = None
try:
def get_netdev_mac(device):
try:
- return read1("/sys/class/net/%s/address" % device)
+ return read1("%s/sys/class/net/%s/address" % (root_prefix(), device))
except:
# Probably no such device.
return None
def get_netdev_tx_queue_len(device):
try:
- return int(read1("/sys/class/net/%s/tx_queue_len" % device))
+ return int(read1("%s/sys/class/net/%s/tx_queue_len" % (root_prefix(), device)))
except:
# Probably no such device.
return None
def get_netdev_by_mac(mac):
- for device in os.listdir("/sys/class/net"):
+ for device in os.listdir(root_prefix() + "/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 None
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))
+ raise Error("Trying to rename %s to %s - This functionality has been removed" % (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))
- pifrec = db.get_pif_record(pif)
+ pifrec = db().get_pif_record(pif)
device = pifrec['device']
mac = pifrec['MAC']
# IP Network Devices -- network devices with IP configuration
#
-def pif_ipdev_name(pif):
- """Return the ipdev name associated with pif"""
- pifrec = db.get_pif_record(pif)
- nwrec = db.get_network_record(pifrec['network'])
-
- if nwrec['bridge']:
- # TODO: sanity check that nwrec['bridgeless'] != 'true'
- return nwrec['bridge']
- else:
- # TODO: sanity check that nwrec['bridgeless'] == 'true'
- return pif_netdev_name(pif)
-
def ifdown(netdev):
"""Bring down a network interface"""
- if debug_mode():
- return
if not netdev_exists(netdev):
log("ifdown: device %s does not exist, ignoring" % netdev)
return
- if not os.path.exists("/etc/sysconfig/network-scripts/ifcfg-%s" % netdev):
- log("ifdown: device %s exists but ifcfg %s does not" % (netdev,netdev))
- netdev_down(netdev)
+ if not os.path.exists("%s/etc/sysconfig/network-scripts/ifcfg-%s" % (root_prefix(), netdev)):
+ log("ifdown: device %s exists but ifcfg-%s does not" % (netdev,netdev))
+ run_command(["/sbin/ifconfig", netdev, 'down'])
+ return
run_command(["/sbin/ifdown", netdev])
def ifup(netdev):
"""Bring up a network interface"""
- if debug_mode():
- return
- if not netdev_exists(netdev):
- raise Error("ifup: device %s does not exist, ignoring" % netdev)
- if not os.path.exists("/etc/sysconfig/network-scripts/ifcfg-%s" % netdev):
+ if not os.path.exists(root_prefix() + "/etc/sysconfig/network-scripts/ifcfg-%s" % netdev):
raise Error("ifup: device %s exists but ifcfg-%s does not" % (netdev,netdev))
+ d = os.getenv("DHCLIENTARGS","")
+ if os.path.exists("/etc/firstboot.d/data/firstboot_in_progress"):
+ os.putenv("DHCLIENTARGS", d + " -T 240 " )
run_command(["/sbin/ifup", netdev])
+ os.putenv("DHCLIENTARGS", d )
#
-# Bridges
+#
#
-def pif_bridge_name(pif):
- """Return the bridge name of a pif.
-
- PIF must not be a VLAN and must be a bridged PIF."""
-
- pifrec = db.get_pif_record(pif)
+def pif_rename_physical_devices(pif):
+ if pif_is_tunnel(pif):
+ return
if pif_is_vlan(pif):
- raise Error("PIF %(uuid)s cannot be a bridge, VLAN is %(VLAN)s" % pifrec)
-
- nwrec = db.get_network_record(pifrec['network'])
+ pif = pif_get_vlan_slave(pif)
- if nwrec['bridge']:
- return nwrec['bridge']
+ if pif_is_bond(pif):
+ pifs = pif_get_bond_slaves(pif)
else:
- raise Error("PIF %(uuid)s does not have a bridge name" % pifrec)
-
-#
-# PIF miscellanea
-#
-
-def pif_currently_in_use(pif):
- """Determine if a PIF is currently in use.
+ pifs = [pif]
- A PIF is determined to be currently in use if
- - PIF.currently-attached is true
- - Any bond master is currently attached
- - Any VLAN master is currently attached
- """
- rec = db.get_pif_record(pif)
- if rec['currently_attached']:
- log("configure_datapath: %s is currently attached" % (pif_netdev_name(pif)))
- return True
- for b in pif_get_bond_masters(pif):
- if pif_currently_in_use(b):
- log("configure_datapath: %s is in use by BOND master %s" % (pif_netdev_name(pif),pif_netdev_name(b)))
- return True
- for v in pif_get_vlan_masters(pif):
- if pif_currently_in_use(v):
- log("configure_datapath: %s is in use by VLAN master %s" % (pif_netdev_name(pif),pif_netdev_name(v)))
- return True
- return False
-
-#
-#
-#
-
-def ethtool_settings(oc):
- settings = []
- if oc.has_key('ethtool-speed'):
- val = oc['ethtool-speed']
- if val in ["10", "100", "1000"]:
- settings += ['speed', val]
- else:
- log("Invalid value for ethtool-speed = %s. Must be 10|100|1000." % val)
- if oc.has_key('ethtool-duplex'):
- val = oc['ethtool-duplex']
- if val in ["10", "100", "1000"]:
- settings += ['duplex', 'val']
- else:
- log("Invalid value for ethtool-duplex = %s. Must be half|full." % val)
- if oc.has_key('ethtool-autoneg'):
- val = oc['ethtool-autoneg']
- if val in ["true", "on"]:
- settings += ['autoneg', 'on']
- elif val in ["false", "off"]:
- settings += ['autoneg', 'off']
- else:
- log("Invalid value for ethtool-autoneg = %s. Must be on|true|off|false." % val)
- offload = []
- for opt in ("rx", "tx", "sg", "tso", "ufo", "gso"):
- if oc.has_key("ethtool-" + opt):
- val = oc["ethtool-" + opt]
- if val in ["true", "on"]:
- offload += [opt, 'on']
- elif val in ["false", "off"]:
- offload += [opt, 'off']
- else:
- log("Invalid value for ethtool-%s = %s. Must be on|true|off|false." % (opt, val))
- return settings,offload
-
-def mtu_setting(oc):
- if oc.has_key('mtu'):
- try:
- int(oc['mtu']) # Check that the value is an integer
- return oc['mtu']
- except ValueError, x:
- log("Invalid value for mtu = %s" % oc['mtu'])
- return None
-
-#
-# Bonded PIFs
-#
-def pif_get_bond_masters(pif):
- """Returns a list of PIFs which are bond masters of this PIF"""
-
- pifrec = db.get_pif_record(pif)
-
- bso = pifrec['bond_slave_of']
-
- # bond-slave-of is currently a single reference but in principle a
- # PIF could be a member of several bonds which are not
- # concurrently attached. Be robust to this possibility.
- if not bso or bso == "OpaqueRef:NULL":
- bso = []
- elif not type(bso) == list:
- bso = [bso]
-
- bondrecs = [db.get_bond_record(bond) for bond in bso]
- bondrecs = [rec for rec in bondrecs if rec]
-
- return [bond['master'] for bond in bondrecs]
-
-def pif_get_bond_slaves(pif):
- """Returns a list of PIFs which make up the given bonded pif."""
-
- pifrec = db.get_pif_record(pif)
-
- bmo = pifrec['bond_master_of']
- if len(bmo) > 1:
- raise Error("Bond-master-of contains too many elements")
-
- if len(bmo) == 0:
- return []
-
- bondrec = db.get_bond_record(bmo[0])
- if not bondrec:
- raise Error("No bond record for bond master PIF")
-
- return bondrec['slaves']
-
-#
-# VLAN PIFs
-#
-
-def pif_is_vlan(pif):
- return db.get_pif_record(pif)['VLAN'] != '-1'
-
-def pif_get_vlan_slave(pif):
- """Find the PIF which is the VLAN slave of pif.
-
-Returns the 'physical' PIF underneath the a VLAN PIF @pif."""
-
- pifrec = db.get_pif_record(pif)
-
- vlan = pifrec['VLAN_master_of']
- if not vlan or vlan == "OpaqueRef:NULL":
- raise Error("PIF is not a VLAN master")
-
- vlanrec = db.get_vlan_record(vlan)
- if not vlanrec:
- raise Error("No VLAN record found for PIF")
-
- return vlanrec['tagged_PIF']
-
-def pif_get_vlan_masters(pif):
- """Returns a list of PIFs which are VLANs on top of the given pif."""
-
- pifrec = db.get_pif_record(pif)
- vlans = [db.get_vlan_record(v) for v in pifrec['VLAN_slave_of']]
- return [v['untagged_PIF'] for v in vlans if v and db.pif_exists(v['untagged_PIF'])]
+ for pif in pifs:
+ netdev_remap_name(pif)
#
# IP device configuration
172.16.0.0/15 via 192.168.0.3 dev xenbr1
172.18.0.0/16 via 192.168.0.4 dev xenbr1
"""
- fname = "route-%s" % interface
if oc.has_key('static-routes'):
- # The key is present - extract comma seperates entries
+ # The key is present - extract comma separates entries
lines = oc['static-routes'].split(',')
else:
# The key is not present, i.e. there are no static routes
lines = []
- child = ConfigurationFile(fname)
+ child = ConfigurationFile("%s/etc/sysconfig/network-scripts/route-%s" % (root_prefix(), interface))
child.write("# DO NOT EDIT: This file (%s) was autogenerated by %s\n" % \
(os.path.basename(child.path()), os.path.basename(sys.argv[0])))
log("Writing network configuration for %s" % ipdev)
- f = ConfigurationFile("ifcfg-%s" % ipdev)
+ f = ConfigurationFile("%s/etc/sysconfig/network-scripts/ifcfg-%s" % (root_prefix(), ipdev))
f.write("# DO NOT EDIT: This file (%s) was autogenerated by %s\n" % \
(os.path.basename(f.path()), os.path.basename(sys.argv[0])))
f.write("XEMANAGED=yes\n")
f.write("DEVICE=%s\n" % ipdev)
f.write("ONBOOT=no\n")
+ f.write("NOZEROCONF=yes\n")
return f
-def ipdev_configure_network(pif):
+def ipdev_configure_network(pif, dp):
"""Write the configuration file for a network.
Writes configuration derived from the network object into the relevant
params:
pif: Opaque_ref of pif
- f : ConfigurationFile(/path/to/ifcfg) to which we append network configuration
+ dp: Datapath object
"""
- pifrec = db.get_pif_record(pif)
- nwrec = db.get_network_record(pifrec['network'])
+ pifrec = db().get_pif_record(pif)
+ nw = pifrec['network']
+ nwrec = db().get_network_record(nw)
ipdev = pif_ipdev_name(pif)
if pifrec.has_key('other_config'):
oc = pifrec['other_config']
- f.write("TYPE=Ethernet\n")
+ dp.configure_ipdev(f)
+
if pifrec['ip_configuration_mode'] == "DHCP":
f.write("BOOTPROTO=dhcp\n")
f.write("PERSISTENT_DHCLIENT=yes\n")
if len(offload):
f.write("ETHTOOL_OFFLOAD_OPTS=\"%s\"\n" % str.join(" ", offload))
- mtu = mtu_setting(nwrec['other_config'])
- if mtu:
- f.write("MTU=%s\n" % mtu)
-
ipdev_configure_static_routes(ipdev, nwrec['other_config'], f)
+ mtu = mtu_setting(nw, "Network", nwrec['other_config'])
+ if mtu:
+ f.write("MTU=%s\n" % mtu)
+
+
if pifrec.has_key('DNS') and pifrec['DNS'] != "":
ServerList = pifrec['DNS'].split(",")
for i in range(len(ServerList)): f.write("DNS%d=%s\n" % (i+1, ServerList[i]))
if oc and oc.has_key('domain'):
f.write("DOMAIN='%s'\n" % oc['domain'].replace(',', ' '))
- # We only allow one ifcfg-* to have PEERDNS=yes and there can be
- # only one GATEWAYDEV in /etc/sysconfig/network.
+ # There can be only one DNSDEV and one GATEWAYDEV in /etc/sysconfig/network.
#
# The peerdns pif will be the one with
# pif::other-config:peerdns=true, or the mgmt pif if none have
# pif::other-config:defaultroute=true, or the mgmt pif if none
# have this set.
- # Work out which pif on this host should be the one with
- # PEERDNS=yes and which should be the GATEWAYDEV
+ # Work out which pif on this host should be the DNSDEV and which
+ # should be the GATEWAYDEV
#
- # Note: we prune out the bond master pif (if it exists). This is
+ # Note: we prune out the bond master pif (if it exists). 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
- not __pif in pif_get_bond_masters(pif) ]
- other_pifs_on_host = [ __pif for __pif in pifs_on_host if __pif != pif ]
- peerdns_pif = None
- defaultroute_pif = None
+ pifs_on_host = [p for p in db().get_all_pifs() if not p in pif_get_bond_masters(pif)]
+
+ # now prune out bond slaves as they are not connected to the IP
+ # stack and so cannot be used as gateway or DNS devices.
+ pifs_on_host = [ p for p in pifs_on_host if len(pif_get_bond_masters(p)) == 0]
# loop through all the pifs on this host looking for one with
# other-config:peerdns = true, and one with
# other-config:default-route=true
+ peerdns_pif = None
+ defaultroute_pif = None
for __pif in pifs_on_host:
- __pifrec = db.get_pif_record(__pif)
+ __pifrec = db().get_pif_record(__pif)
__oc = __pifrec['other_config']
if __oc.has_key('peerdns') and __oc['peerdns'] == 'true':
if peerdns_pif == None:
peerdns_pif = __pif
else:
log('Warning: multiple pifs with "peerdns=true" - choosing %s and ignoring %s' % \
- (db.get_pif_record(peerdns_pif)['device'], __pifrec['device']))
+ (db().get_pif_record(peerdns_pif)['device'], __pifrec['device']))
if __oc.has_key('defaultroute') and __oc['defaultroute'] == 'true':
if defaultroute_pif == None:
defaultroute_pif = __pif
else:
log('Warning: multiple pifs with "defaultroute=true" - choosing %s and ignoring %s' % \
- (db.get_pif_record(defaultroute_pif)['device'], __pifrec['device']))
+ (db().get_pif_record(defaultroute_pif)['device'], __pifrec['device']))
# If no pif is explicitly specified then use the mgmt pif for
# peerdns/defaultroute.
if defaultroute_pif == None:
defaultroute_pif = management_pif
- # Update all the other network's ifcfg files and ensure
- # consistency.
- for __pif in other_pifs_on_host:
- __f = ipdev_open_ifcfg(__pif)
- peerdns_line_wanted = 'PEERDNS=%s\n' % ((__pif == peerdns_pif) and 'yes' or 'no')
- lines = __f.readlines()
-
- if not peerdns_line_wanted in lines:
- # the PIF selected for DNS has changed and as a result this ifcfg file needs rewriting
- for line in lines:
- if not line.lstrip().startswith('PEERDNS'):
- __f.write(line)
- log("Setting %s in %s" % (peerdns_line_wanted.strip(), __f.path()))
- __f.write(peerdns_line_wanted)
- __f.close()
- f.attach_child(__f)
-
- else:
- # There is no need to change this ifcfg file. So don't attach_child.
- pass
-
- # ... and for this pif too
- f.write('PEERDNS=%s\n' % ((pif == peerdns_pif) and 'yes' or 'no'))
-
- # Update gatewaydev
- fnetwork = ConfigurationFile("network", "/etc/sysconfig")
- for line in fnetwork.readlines():
- if line.lstrip().startswith('GATEWAY') :
- continue
- fnetwork.write(line)
- if defaultroute_pif:
- gatewaydev = pif_ipdev_name(defaultroute_pif)
- if not gatewaydev:
- gatewaydev = pif_netdev_name(defaultroute_pif)
- fnetwork.write('GATEWAYDEV=%s\n' % gatewaydev)
- fnetwork.close()
- f.attach_child(fnetwork)
-
- return f
-
-#
-# Datapath Configuration
-#
-
-def pif_datapath(pif):
- """Return the OpenFlow datapath name associated with pif.
-For a non-VLAN PIF, the datapath name is the bridge name.
-For a VLAN PIF, the datapath name is the bridge name for the PIF's VLAN slave.
-"""
- if pif_is_vlan(pif):
- return pif_datapath(pif_get_vlan_slave(pif))
-
- pifrec = db.get_pif_record(pif)
- nwrec = db.get_network_record(pifrec['network'])
- if not nwrec['bridge']:
- raise Error("datapath PIF cannot be bridgeless")
- else:
- return pif
-
-def datapath_get_physical_pifs(pif):
- """Return the PIFs for the physical network device(s) associated with a datapath PIF.
-For a bond master PIF, these are the bond slave PIFs.
-For a non-VLAN, non-bond master PIF, the PIF is its own physical device PIF.
-
-A VLAN PIF cannot be a datapath PIF.
-"""
- pifrec = db.get_pif_record(pif)
-
- if pif_is_vlan(pif):
- raise Error("get-physical-pifs should not get passed a VLAN")
- elif len(pifrec['bond_master_of']) != 0:
- return pif_get_bond_slaves(pif)
- else:
- return [pif]
-
-def datapath_deconfigure_physical(netdev):
- return ['--', '--if-exists', 'del-port', netdev]
-
-def datapath_configure_bond(pif,slaves):
- bridge = pif_bridge_name(pif)
- pifrec = db.get_pif_record(pif)
- interface = pif_netdev_name(pif)
-
- argv = ['--', '--fake-iface', 'add-bond', bridge, interface]
- for slave in slaves:
- argv += [pif_netdev_name(slave)]
-
- # XXX need ovs-vsctl support
- #if pifrec['MAC'] != "":
- # argv += ['--add=port.%s.mac=%s' % (interface, pifrec['MAC'])]
-
- # Bonding options.
- bond_options = {
- "mode": "balance-slb",
- "miimon": "100",
- "downdelay": "200",
- "updelay": "31000",
- "use_carrier": "1",
- }
- # override defaults with values from other-config whose keys
- # being with "bond-"
- oc = pifrec['other_config']
- overrides = filter(lambda (key,val):
- key.startswith("bond-"), oc.items())
- overrides = map(lambda (key,val): (key[5:], val), overrides)
- bond_options.update(overrides)
- for (name,val) in bond_options.items():
- # XXX need ovs-vsctl support for bond options
- #argv += ["--add=bonding.%s.%s=%s" % (interface, name, val)]
- pass
- return argv
-
-def datapath_deconfigure_bond(netdev):
- return ['--', '--if-exists', 'del-port', netdev]
+ is_dnsdev = peerdns_pif == pif
+ is_gatewaydev = defaultroute_pif == pif
+
+ if is_dnsdev or is_gatewaydev:
+ fnetwork = ConfigurationFile(root_prefix() + "/etc/sysconfig/network")
+ for line in fnetwork.readlines():
+ if is_dnsdev and line.lstrip().startswith('DNSDEV='):
+ fnetwork.write('DNSDEV=%s\n' % ipdev)
+ is_dnsdev = False
+ elif is_gatewaydev and line.lstrip().startswith('GATEWAYDEV='):
+ fnetwork.write('GATEWAYDEV=%s\n' % ipdev)
+ is_gatewaydev = False
+ else:
+ fnetwork.write(line)
-def datapath_deconfigure_ipdev(interface):
- return ['--', '--if-exists', 'del-port', interface]
+ if is_dnsdev:
+ fnetwork.write('DNSDEV=%s\n' % ipdev)
+ if is_gatewaydev:
+ fnetwork.write('GATEWAYDEV=%s\n' % ipdev)
-def datapath_modify_config(commands):
- if debug_mode():
- log("modifying configuration:")
- for c in commands:
- log(" %s" % c)
+ fnetwork.close()
+ f.attach_child(fnetwork)
- rc = run_command(['/usr/bin/ovs-vsctl'] + ['--timeout=20']
- + [c for c in commands if not c.startswith('#')])
- if not rc:
- raise Error("Failed to modify vswitch configuration")
- return True
+ return f
#
-# Toplevel Datapath Configuration.
+# Toplevel actions
#
-def configure_datapath(pif):
- """Bring up the datapath configuration for PIF.
-
- Should be careful not to glitch existing users of the datapath, e.g. other VLANs etc.
-
- Should take care of tearing down other PIFs which encompass common physical devices.
-
- Returns a tuple containing
- - A list containing the necessary cfgmod command line arguments
- - A list of additional devices which should be brought up after
- the configuration is applied.
- """
-
- cfgmod_argv = []
- extra_up_ports = []
-
- bridge = pif_bridge_name(pif)
-
- physical_devices = datapath_get_physical_pifs(pif)
-
- # Determine additional devices to deconfigure.
- #
- # Given all physical devices which are part of this PIF we need to
- # consider:
- # - any additional bond which a physical device is part of.
- # - any additional physical devices which are part of an additional bond.
- #
- # Any of these which are not currently in use should be brought
- # down and deconfigured.
- extra_down_bonds = []
- extra_down_ports = []
- for p in physical_devices:
- for bond in pif_get_bond_masters(p):
- if bond == pif:
- log("configure_datapath: leaving bond %s up" % pif_netdev_name(bond))
- continue
- if bond in extra_down_bonds:
- continue
- if db.get_pif_record(bond)['currently_attached']:
- log("configure_datapath: implicitly tearing down currently-attached bond %s" % pif_netdev_name(bond))
-
- extra_down_bonds += [bond]
-
- for s in pif_get_bond_slaves(bond):
- if s in physical_devices:
- continue
- if s in extra_down_ports:
- continue
- if pif_currently_in_use(s):
- continue
- extra_down_ports += [s]
-
- log("configure_datapath: bridge - %s" % bridge)
- log("configure_datapath: physical - %s" % [pif_netdev_name(p) for p in physical_devices])
- log("configure_datapath: extra ports - %s" % [pif_netdev_name(p) for p in extra_down_ports])
- log("configure_datapath: extra bonds - %s" % [pif_netdev_name(p) for p in extra_down_bonds])
-
- # Need to fully deconfigure any bridge which any of the:
- # - physical devices
- # - bond devices
- # - sibling devices
- # refers to
- for brpif in physical_devices + extra_down_ports + extra_down_bonds:
- if brpif == pif:
- continue
- b = pif_bridge_name(brpif)
- ifdown(b)
- cfgmod_argv += ['# remove bridge %s' % b]
- cfgmod_argv += ['--', '--if-exists', 'del-br', b]
-
- for n in extra_down_ports:
- dev = pif_netdev_name(n)
- cfgmod_argv += ['# deconfigure sibling physical device %s' % dev]
- cfgmod_argv += datapath_deconfigure_physical(dev)
- netdev_down(dev)
-
- for n in extra_down_bonds:
- dev = pif_netdev_name(n)
- cfgmod_argv += ['# deconfigure bond device %s' % dev]
- cfgmod_argv += datapath_deconfigure_bond(dev)
- netdev_down(dev)
-
- for p in physical_devices:
- dev = pif_netdev_name(p)
- cfgmod_argv += ['# deconfigure physical port %s' % dev]
- cfgmod_argv += datapath_deconfigure_physical(dev)
-
- # Check the MAC address of each network device and remap if
- # necessary to make names match our expectations.
- for p in physical_devices:
- netdev_remap_name(p)
-
- # Bring up physical devices early, because ovs-vswitchd initially
- # enables or disables bond slaves based on whether carrier is
- # detected when they are added, and a network device that is down
- # always reports "no carrier".
- for p in physical_devices:
- oc = db.get_pif_record(p)['other_config']
-
- dev = pif_netdev_name(p)
-
- mtu = mtu_setting(oc)
-
- netdev_up(dev, mtu)
-
- settings, offload = ethtool_settings(oc)
- if len(settings):
- run_command(['/sbin/ethtool', '-s', dev] + settings)
- if len(offload):
- run_command(['/sbin/ethtool', '-K', dev] + offload)
+def action_up(pif, force):
+ pifrec = db().get_pif_record(pif)
- # XXX It seems like the following should not be necessary...
- cfgmod_argv += ['--', '--if-exists', 'del-br', bridge]
-
- if pif_is_vlan(pif):
- datapath = pif_datapath(pif)
- vlan = db.get_pif_record(pif)['VLAN']
- cfgmod_argv += ['--', 'add-br', bridge, datapath, vlan]
- else:
- cfgmod_argv += ['--', 'add-br', bridge]
-
- if len(physical_devices) > 1:
- cfgmod_argv += ['# deconfigure bond %s' % pif_netdev_name(pif)]
- cfgmod_argv += datapath_deconfigure_bond(pif_netdev_name(pif))
- cfgmod_argv += ['# configure bond %s' % pif_netdev_name(pif)]
- cfgmod_argv += datapath_configure_bond(pif, physical_devices)
- extra_up_ports += [pif_netdev_name(pif)]
- else:
- iface = pif_netdev_name(physical_devices[0])
- cfgmod_argv += ['# add physical device %s' % iface]
- cfgmod_argv += ['--', 'add-port', bridge, iface]
-
- return cfgmod_argv,extra_up_ports
-
-def deconfigure_datapath(pif):
- cfgmod_argv = []
-
- bridge = pif_bridge_name(pif)
-
- physical_devices = datapath_get_physical_pifs(pif)
-
- log("deconfigure_datapath: bridge - %s" % bridge)
- log("deconfigure_datapath: physical devices - %s" % [pif_netdev_name(p) for p in physical_devices])
-
- for p in physical_devices:
- dev = pif_netdev_name(p)
- cfgmod_argv += ['# deconfigure physical port %s' % dev]
- cfgmod_argv += datapath_deconfigure_physical(dev)
- netdev_down(dev)
+ ipdev = pif_ipdev_name(pif)
+ dp = DatapathFactory()(pif)
- if len(physical_devices) > 1:
- cfgmod_argv += ['# deconfigure bond %s' % pif_netdev_name(pif)]
- cfgmod_argv += datapath_deconfigure_bond(pif_netdev_name(pif))
+ log("action_up: %s" % ipdev)
- cfgmod_argv += ['# deconfigure bridge %s' % bridge]
- cfgmod_argv += ['--', '--if-exists', 'del-br', bridge]
-
- return cfgmod_argv
+ f = ipdev_configure_network(pif, dp)
-#
-# Toplevel actions
-#
+ dp.preconfigure(f)
-def action_up(pif):
- pifrec = db.get_pif_record(pif)
- cfgmod_argv = []
- extra_ports = []
+ f.close()
- ipdev = pif_ipdev_name(pif)
- dp = pif_datapath(pif)
- bridge = pif_bridge_name(dp)
+ pif_rename_physical_devices(pif)
- log("action_up: %s on bridge %s" % (ipdev, bridge))
-
- ifdown(ipdev)
+ # if we are not forcing the interface up then attempt to tear down
+ # any existing devices which might interfere with brinign this one
+ # up.
+ if not force:
+ ifdown(ipdev)
- if dp:
- c,e = configure_datapath(dp)
- cfgmod_argv += c
- extra_ports += e
-
- xs_network_uuids = []
- for nwpif in db.get_pifs_by_device(db.get_pif_record(pif)['device']):
- rec = db.get_pif_record(nwpif)
-
- # When state is read from dbcache PIF.currently_attached
- # is always assumed to be false... Err on the side of
- # listing even detached networks for the time being.
- #if nwpif != pif and not rec['currently_attached']:
- # log("Network PIF %s not currently attached (%s)" % (rec['uuid'],pifrec['uuid']))
- # continue
- nwrec = db.get_network_record(rec['network'])
- xs_network_uuids += [nwrec['uuid']]
- cfgmod_argv += ['# configure xs-network-uuids']
- cfgmod_argv += ['--', 'br-set-external-id', bridge,
- 'xs-network-uuids', ';'.join(xs_network_uuids)]
-
- if ipdev != bridge:
- cfgmod_argv += ["# deconfigure ipdev %s" % ipdev]
- cfgmod_argv += datapath_deconfigure_ipdev(ipdev)
- cfgmod_argv += ["# reconfigure ipdev %s" % ipdev]
- cfgmod_argv += ['--', 'add-port', bridge, ipdev]
-
- f = ipdev_configure_network(pif)
- f.close()
+ dp.bring_down_existing()
- # Apply updated configuration.
try:
f.apply()
- datapath_modify_config(cfgmod_argv)
+ dp.configure()
ifup(ipdev)
- for p in extra_ports:
- netdev_up(p)
+ dp.post()
# Update /etc/issue (which contains the IP address of the management interface)
- os.system("/sbin/update-issue")
+ os.system(root_prefix() + "/sbin/update-issue")
f.commit()
except Error, e:
raise
def action_down(pif):
- pifrec = db.get_pif_record(pif)
- cfgmod_argv = []
-
ipdev = pif_ipdev_name(pif)
- dp = pif_datapath(pif)
- bridge = pif_bridge_name(dp)
-
- log("action_down: %s on bridge %s" % (ipdev, bridge))
+ dp = DatapathFactory()(pif)
- ifdown(ipdev)
-
- if dp:
- nw = db.get_pif_record(pif)['network']
- nwrec = db.get_network_record(nw)
+ log("action_down: %s" % ipdev)
- log("deconfigure ipdev %s on %s" % (ipdev,bridge))
- cfgmod_argv += ["# deconfigure ipdev %s" % ipdev]
- cfgmod_argv += datapath_deconfigure_ipdev(ipdev)
+ ifdown(ipdev)
- f = ipdev_open_ifcfg(pif)
- f.unlink()
+ dp.bring_down()
- if pif_is_vlan(pif):
- br = ConfigurationFile("br-%s" % bridge, vswitch_state_dir)
- br.unlink()
- f.attach_child(br)
-
- # If the VLAN's slave is attached, leave datapath setup.
- slave = pif_get_vlan_slave(pif)
- if db.get_pif_record(slave)['currently_attached']:
- log("action_down: vlan slave is currently attached")
- dp = None
-
- # If the VLAN's slave has other VLANs that are attached, leave datapath setup.
- for master in pif_get_vlan_masters(slave):
- if master != pif and db.get_pif_record(master)['currently_attached']:
- log("action_down: vlan slave has other master: %s" % pif_netdev_name(master))
- dp = None
-
- # Otherwise, take down the datapath too (fall through)
- if dp:
- log("action_down: no more masters, bring down slave %s" % bridge)
- else:
- # Stop here if this PIF has attached VLAN masters.
- masters = [db.get_pif_record(m)['VLAN'] for m in pif_get_vlan_masters(pif) if db.get_pif_record(m)['currently_attached']]
- if len(masters) > 0:
- log("Leaving datapath %s up due to currently attached VLAN masters %s" % (bridge, masters))
- dp = None
+def action_rewrite():
+ DatapathFactory().rewrite()
+
+# This is useful for reconfiguring the mgmt interface after having lost connectivity to the pool master
+def action_force_rewrite(bridge, config):
+ def getUUID():
+ import subprocess
+ uuid,_ = subprocess.Popen(['uuidgen'], stdout = subprocess.PIPE).communicate()
+ return uuid.strip()
- if dp:
- cfgmod_argv += deconfigure_datapath(dp)
+ # Notes:
+ # 1. that this assumes the interface is bridged
+ # 2. If --gateway is given it will make that the default gateway for the host
+ # extract the configuration
try:
- f.apply()
+ mode = config['mode']
+ mac = config['mac']
+ interface = config['device']
+ except:
+ raise Usage("Please supply --mode, --mac and --device")
- datapath_modify_config(cfgmod_argv)
+ if mode == 'static':
+ try:
+ netmask = config['netmask']
+ ip = config['ip']
+ except:
+ raise Usage("Please supply --netmask and --ip")
+ try:
+ gateway = config['gateway']
+ except:
+ gateway = None
+ elif mode != 'dhcp':
+ raise Usage("--mode must be either static or dhcp")
- f.commit()
- except Error, e:
- log("action_down failed to apply changes: %s" % e.msg)
- f.revert()
- raise
+ if config.has_key('vlan'):
+ is_vlan = True
+ vlan_slave, vlan_vid = config['vlan'].split('.')
+ else:
+ is_vlan = False
+
+ if is_vlan:
+ raise Error("Force rewrite of VLAN not implemented")
+
+ log("Configuring %s using %s configuration" % (bridge, mode))
+
+ f = ConfigurationFile(root_prefix() + dbcache_file)
+
+ pif_uuid = getUUID()
+ network_uuid = getUUID()
+
+ f.write('<?xml version="1.0" ?>\n')
+ f.write('<xenserver-network-configuration>\n')
+ f.write('\t<pif ref="OpaqueRef:%s">\n' % pif_uuid)
+ f.write('\t\t<network>OpaqueRef:%s</network>\n' % network_uuid)
+ f.write('\t\t<management>True</management>\n')
+ f.write('\t\t<uuid>%sPif</uuid>\n' % interface)
+ f.write('\t\t<bond_slave_of>OpaqueRef:NULL</bond_slave_of>\n')
+ f.write('\t\t<bond_master_of/>\n')
+ f.write('\t\t<VLAN_slave_of/>\n')
+ f.write('\t\t<VLAN_master_of>OpaqueRef:NULL</VLAN_master_of>\n')
+ f.write('\t\t<VLAN>-1</VLAN>\n')
+ f.write('\t\t<tunnel_access_PIF_of/>\n')
+ f.write('\t\t<tunnel_transport_PIF_of/>\n')
+ f.write('\t\t<device>%s</device>\n' % interface)
+ f.write('\t\t<MAC>%s</MAC>\n' % mac)
+ f.write('\t\t<other_config/>\n')
+ if mode == 'dhcp':
+ f.write('\t\t<ip_configuration_mode>DHCP</ip_configuration_mode>\n')
+ f.write('\t\t<IP></IP>\n')
+ f.write('\t\t<netmask></netmask>\n')
+ f.write('\t\t<gateway></gateway>\n')
+ f.write('\t\t<DNS></DNS>\n')
+ elif mode == 'static':
+ f.write('\t\t<ip_configuration_mode>Static</ip_configuration_mode>\n')
+ f.write('\t\t<IP>%s</IP>\n' % ip)
+ f.write('\t\t<netmask>%s</netmask>\n' % netmask)
+ if gateway is not None:
+ f.write('\t\t<gateway>%s</gateway>\n' % gateway)
+ f.write('\t\t<DNS></DNS>\n')
+ else:
+ raise Error("Unknown mode %s" % mode)
+ f.write('\t</pif>\n')
+
+ f.write('\t<network ref="OpaqueRef:%s">\n' % network_uuid)
+ f.write('\t\t<uuid>InitialManagementNetwork</uuid>\n')
+ f.write('\t\t<PIFs>\n')
+ f.write('\t\t\t<PIF>OpaqueRef:%s</PIF>\n' % pif_uuid)
+ f.write('\t\t</PIFs>\n')
+ f.write('\t\t<bridge>%s</bridge>\n' % bridge)
+ f.write('\t\t<other_config/>\n')
+ f.write('\t</network>\n')
+ f.write('</xenserver-network-configuration>\n')
-def action_rewrite(pif):
- f = ipdev_configure_network(pif)
f.close()
+
try:
f.apply()
f.commit()
f.revert()
raise
-def action_force_rewrite(bridge, config):
- raise Error("Force rewrite is not implemented yet.")
-
def main(argv=None):
- global output_directory, management_pif
+ global management_pif
session = None
pif_uuid = None
try:
try:
shortops = "h"
- longops = [ "output-directory=",
- "pif=", "pif-uuid=",
+ longops = [ "pif=", "pif-uuid=",
"session=",
"force=",
"force-interface=",
"management",
- "device=", "mode=", "ip=", "netmask=", "gateway=",
+ "mac=", "device=", "mode=", "ip=", "netmask=", "gateway=",
+ "root-prefix=",
+ "no-syslog",
"help" ]
arglist, args = getopt.gnu_getopt(argv[1:], shortops, longops)
except getopt.GetoptError, msg:
force_rewrite_config = {}
for o,a in arglist:
- if o == "--output-directory":
- output_directory = a
- elif o == "--pif":
+ if o == "--pif":
pif = a
elif o == "--pif-uuid":
pif_uuid = a
force_interface = a
elif o == "--management":
force_management = True
- elif o in ["--device", "--mode", "--ip", "--netmask", "--gateway"]:
+ elif o in ["--mac", "--device", "--mode", "--ip", "--netmask", "--gateway"]:
force_rewrite_config[o[2:]] = a
+ elif o == "--root-prefix":
+ set_root_prefix(a)
+ elif o == "--no-syslog":
+ set_log_destination("stderr")
elif o == "-h" or o == "--help":
print __doc__ % {'command-name': os.path.basename(argv[0])}
return 0
- if not debug_mode():
+ if get_log_destination() == "syslog":
syslog.openlog(os.path.basename(argv[0]))
log("Called as " + str.join(" ", argv))
+
if len(args) < 1:
raise Usage("Required option <action> not present")
if len(args) > 1:
# backwards compatibility
if action == "rewrite-configuration": action = "rewrite"
- if output_directory and ( session or pif ):
- raise Usage("--session/--pif cannot be used with --output-directory")
if ( session or pif ) and pif_uuid:
raise Usage("--session/--pif and --pif-uuid are mutually exclusive.")
if ( session and not pif ) or ( not session and pif ):
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 (action == "rewrite") and (pif or pif_uuid ):
+ raise Usage("rewrite action does not take --pif or --pif-uuid")
+
global db
if force_interface:
log("Force interface %s %s" % (force_interface, action))
if action == "rewrite":
action_force_rewrite(force_interface, force_rewrite_config)
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)
- pif = db.get_pif_by_bridge(force_interface)
- management_pif = db.get_management_pif()
+ db_init_from_cache(dbcache_file)
+ pif = db().get_pif_by_bridge(force_interface)
+ management_pif = db().get_management_pif()
if action == "up":
- action_up(pif)
+ action_up(pif, True)
elif action == "down":
action_down(pif)
else:
raise Error("Unknown action %s" % action)
else:
- db = DatabaseCache(session_ref=session)
+ db_init_from_xenapi(session)
if pif_uuid:
- pif = db.get_pif_by_uuid(pif_uuid)
+ pif = db().get_pif_by_uuid(pif_uuid)
- if action == "rewrite" and not pif:
- pass
+ if action == "rewrite":
+ action_rewrite()
else:
if not pif:
raise Usage("No PIF given")
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()
+ pifrec = db().get_pif_record(pif)
+ management_pif = db().get_management_pif()
log_pif_action(action, pif)
return 0
if action == "up":
- action_up(pif)
+ action_up(pif, False)
elif action == "down":
action_down(pif)
- elif action == "rewrite":
- action_rewrite(pif)
else:
raise Error("Unknown action %s" % action)
# Save cache.
- db.save(dbcache_file)
+ db().save(dbcache_file)
except Usage, err:
print >>sys.stderr, err.msg
for exline in err:
log(exline)
- if not debug_mode():
- syslog.closelog()
+ syslog.closelog()
sys.exit(rc)