3 # Copyright (c) 2008,2009 Citrix Systems, Inc. All rights reserved.
4 # Copyright (c) 2009 Nicira Networks.
8 %(command-name)s <PIF> up
9 %(command-name)s <PIF> down
10 %(command-name)s [<PIF>] rewrite
11 %(command-name)s --force <BRIDGE> up
12 %(command-name)s --force <BRIDGE> down
13 %(command-name)s --force <BRIDGE> rewrite --device=<INTERFACE> <CONFIG>
14 %(command-name)s --force all down
16 where <PIF> is one of:
17 --session <SESSION-REF> --pif <PIF-REF>
19 and <CONFIG> is one of:
21 --mode=static --ip=<IPADDR> --netmask=<NM> [--gateway=<GW>]
24 --session A session reference to use to access the xapi DB
25 --pif A PIF reference within the session.
26 --pif-uuid The UUID of a PIF.
27 --force An interface name.
31 # Undocumented parameters for test & dev:
33 # --output-directory=<DIR> Write configuration to <DIR>. Also disables actually
34 # raising/lowering the interfaces
39 # 1. Every pif belongs to exactly one network
40 # 2. Every network has zero or one pifs
41 # 3. A network may have an associated bridge, allowing vifs to be attached
42 # 4. A network may be bridgeless (there's no point having a bridge over a storage pif)
45 import os, sys, getopt, time, signal
50 from xml.dom.minidom import getDOMImplementation
51 from xml.dom.minidom import parse as parseXML
53 output_directory = None
58 vswitch_state_dir = "/var/lib/openvswitch/"
59 dbcache_file = vswitch_state_dir + "dbcache"
62 # Debugging and Logging.
66 return output_directory is not None
74 def log_pif_action(action, pif):
75 pifrec = db.get_pif_record(pif)
77 rec['uuid'] = pifrec['uuid']
78 rec['ip_configuration_mode'] = pifrec['ip_configuration_mode']
79 rec['action'] = action
80 rec['pif_netdev_name'] = pif_netdev_name(pif)
81 rec['message'] = "Bring %(action)s PIF %(uuid)s" % rec
82 log("%(message)s: %(pif_netdev_name)s configured as %(ip_configuration_mode)s" % rec)
85 def run_command(command):
86 log("Running command: " + ' '.join(command))
87 rc = os.spawnl(os.P_WAIT, command[0], *command)
89 log("Command failed %d: " % rc + ' '.join(command))
97 class Usage(Exception):
98 def __init__(self, msg):
99 Exception.__init__(self)
102 class Error(Exception):
103 def __init__(self, msg):
104 Exception.__init__(self)
108 # Configuration File Handling.
111 class ConfigurationFile(object):
112 """Write a file, tracking old and new versions.
114 Supports writing a new version of a file and applying and
115 reverting those changes.
118 __STATE = {"OPEN":"OPEN",
119 "NOT-APPLIED":"NOT-APPLIED", "APPLIED":"APPLIED",
120 "REVERTED":"REVERTED", "COMMITTED": "COMMITTED"}
122 def __init__(self, fname, path="/etc/sysconfig/network-scripts"):
124 self.__state = self.__STATE['OPEN']
129 dirname = output_directory
133 self.__path = os.path.join(dirname, fname)
134 self.__oldpath = os.path.join(dirname, "." + fname + ".xapi-old")
135 self.__newpath = os.path.join(dirname, "." + fname + ".xapi-new")
136 self.__unlink = False
138 self.__f = open(self.__newpath, "w")
140 def attach_child(self, child):
141 self.__children.append(child)
148 return open(self.path()).readlines()
152 def write(self, args):
153 if self.__state != self.__STATE['OPEN']:
154 raise Error("Attempt to write to file in state %s" % self.__state)
158 if self.__state != self.__STATE['OPEN']:
159 raise Error("Attempt to unlink file in state %s" % self.__state)
162 self.__state = self.__STATE['NOT-APPLIED']
165 if self.__state != self.__STATE['OPEN']:
166 raise Error("Attempt to close file in state %s" % self.__state)
169 self.__state = self.__STATE['NOT-APPLIED']
172 if self.__state != self.__STATE['NOT-APPLIED']:
173 raise Error("Attempt to compare file in state %s" % self.__state)
178 if self.__state != self.__STATE['NOT-APPLIED']:
179 raise Error("Attempt to apply configuration from state %s" % self.__state)
181 for child in self.__children:
184 log("Applying changes to %s configuration" % self.__fname)
186 # Remove previous backup.
187 if os.access(self.__oldpath, os.F_OK):
188 os.unlink(self.__oldpath)
190 # Save current configuration.
191 if os.access(self.__path, os.F_OK):
192 os.link(self.__path, self.__oldpath)
193 os.unlink(self.__path)
195 # Apply new configuration.
196 assert(os.path.exists(self.__newpath))
197 if not self.__unlink:
198 os.link(self.__newpath, self.__path)
200 pass # implicit unlink of original file
202 # Remove temporary file.
203 os.unlink(self.__newpath)
205 self.__state = self.__STATE['APPLIED']
208 if self.__state != self.__STATE['APPLIED']:
209 raise Error("Attempt to revert configuration from state %s" % self.__state)
211 for child in self.__children:
214 log("Reverting changes to %s configuration" % self.__fname)
216 # Remove existing new configuration
217 if os.access(self.__newpath, os.F_OK):
218 os.unlink(self.__newpath)
220 # Revert new configuration.
221 if os.access(self.__path, os.F_OK):
222 os.link(self.__path, self.__newpath)
223 os.unlink(self.__path)
225 # Revert to old configuration.
226 if os.access(self.__oldpath, os.F_OK):
227 os.link(self.__oldpath, self.__path)
228 os.unlink(self.__oldpath)
230 # Leave .*.xapi-new as an aid to debugging.
232 self.__state = self.__STATE['REVERTED']
235 if self.__state != self.__STATE['APPLIED']:
236 raise Error("Attempt to commit configuration from state %s" % self.__state)
238 for child in self.__children:
241 log("Committing changes to %s configuration" % self.__fname)
243 if os.access(self.__oldpath, os.F_OK):
244 os.unlink(self.__oldpath)
245 if os.access(self.__newpath, os.F_OK):
246 os.unlink(self.__newpath)
248 self.__state = self.__STATE['COMMITTED']
251 # Helper functions for encoding/decoding database attributes to/from XML.
254 def str_to_xml(xml, parent, tag, val):
255 e = xml.createElement(tag)
256 parent.appendChild(e)
257 v = xml.createTextNode(val)
260 def getText(nodelist):
262 for node in nodelist:
263 if node.nodeType == node.TEXT_NODE:
266 return getText(n.childNodes).strip()
268 def bool_to_xml(xml, parent, tag, val):
270 str_to_xml(xml, parent, tag, "True")
272 str_to_xml(xml, parent, tag, "False")
273 def bool_from_xml(n):
280 raise Error("Unknown boolean value %s" % s)
282 def strlist_to_xml(xml, parent, ltag, itag, val):
283 e = xml.createElement(ltag)
284 parent.appendChild(e)
286 c = xml.createElement(itag)
288 cv = xml.createTextNode(v)
290 def strlist_from_xml(n, ltag, itag):
292 for n in n.childNodes:
293 if n.nodeName == itag:
294 ret.append(str_from_xml(n))
297 def otherconfig_to_xml(xml, parent, val, attrs):
298 otherconfig = xml.createElement("other_config")
299 parent.appendChild(otherconfig)
300 for n,v in val.items():
302 raise Error("Unknown other-config attribute: %s" % n)
303 str_to_xml(xml, otherconfig, n, v)
304 def otherconfig_from_xml(n, attrs):
306 for n in n.childNodes:
307 if n.nodeName in attrs:
308 ret[n.nodeName] = str_from_xml(n)
312 # Definitions of the database objects (and their attributes) used by interface-reconfigure.
314 # Each object is defined by a dictionary mapping an attribute name in
315 # the xapi database to a tuple containing two items:
316 # - a function which takes this attribute and encodes it as XML.
317 # - a function which takes XML and decocdes it into a value.
319 # other-config attributes are specified as a simple array of strings
322 VLAN_XML_TAG = "vlan"
323 BOND_XML_TAG = "bond"
324 NETWORK_XML_TAG = "network"
326 ETHTOOL_OTHERCONFIG_ATTRS = ['ethtool-%s' % x for x in 'autoneg', 'speed', 'duplex', 'rx', 'tx', 'sg', 'tso', 'ufo', 'gso' ]
328 PIF_OTHERCONFIG_ATTRS = [ 'domain', 'peerdns', 'defaultroute', 'mtu', 'static-routes' ] + \
329 [ 'bond-%s' % x for x in 'mode', 'miimon', 'downdelay', 'updelay', 'use_carrier' ] + \
330 ETHTOOL_OTHERCONFIG_ATTRS
332 PIF_ATTRS = { 'uuid': (str_to_xml,str_from_xml),
333 'management': (bool_to_xml,bool_from_xml),
334 'network': (str_to_xml,str_from_xml),
335 'device': (str_to_xml,str_from_xml),
336 'bond_master_of': (lambda x, p, t, v: strlist_to_xml(x, p, 'bond_master_of', 'slave', v),
337 lambda n: strlist_from_xml(n, 'bond_master_of', 'slave')),
338 'bond_slave_of': (str_to_xml,str_from_xml),
339 'VLAN': (str_to_xml,str_from_xml),
340 'VLAN_master_of': (str_to_xml,str_from_xml),
341 'VLAN_slave_of': (lambda x, p, t, v: strlist_to_xml(x, p, 'VLAN_slave_of', 'master', v),
342 lambda n: strlist_from_xml(n, 'VLAN_slave_Of', 'master')),
343 'ip_configuration_mode': (str_to_xml,str_from_xml),
344 'IP': (str_to_xml,str_from_xml),
345 'netmask': (str_to_xml,str_from_xml),
346 'gateway': (str_to_xml,str_from_xml),
347 'DNS': (str_to_xml,str_from_xml),
348 'MAC': (str_to_xml,str_from_xml),
349 'other_config': (lambda x, p, t, v: otherconfig_to_xml(x, p, v, PIF_OTHERCONFIG_ATTRS),
350 lambda n: otherconfig_from_xml(n, PIF_OTHERCONFIG_ATTRS)),
352 # Special case: We write the current value
353 # PIF.currently-attached to the cache but since it will
354 # not be valid when we come to use the cache later
355 # (i.e. after a reboot) we always read it as False.
356 'currently_attached': (bool_to_xml, lambda n: False),
359 VLAN_ATTRS = { 'uuid': (str_to_xml,str_from_xml),
360 'tagged_PIF': (str_to_xml,str_from_xml),
361 'untagged_PIF': (str_to_xml,str_from_xml),
364 BOND_ATTRS = { 'uuid': (str_to_xml,str_from_xml),
365 'master': (str_to_xml,str_from_xml),
366 'slaves': (lambda x, p, t, v: strlist_to_xml(x, p, 'slaves', 'slave', v),
367 lambda n: strlist_from_xml(n, 'slaves', 'slave')),
370 NETWORK_OTHERCONFIG_ATTRS = [ 'mtu', 'static-routes' ] + ETHTOOL_OTHERCONFIG_ATTRS
372 NETWORK_ATTRS = { 'uuid': (str_to_xml,str_from_xml),
373 'bridge': (str_to_xml,str_from_xml),
374 'PIFs': (lambda x, p, t, v: strlist_to_xml(x, p, 'PIFs', 'PIF', v),
375 lambda n: strlist_from_xml(n, 'PIFs', 'PIF')),
376 'other_config': (lambda x, p, t, v: otherconfig_to_xml(x, p, v, NETWORK_OTHERCONFIG_ATTRS),
377 lambda n: otherconfig_from_xml(n, NETWORK_OTHERCONFIG_ATTRS)),
380 class DatabaseCache(object):
381 def __read_xensource_inventory(self):
382 filename = "/etc/xensource-inventory"
383 f = open(filename, "r")
384 lines = [x.strip("\n") for x in f.readlines()]
387 defs = [ (l[:l.find("=")], l[(l.find("=") + 1):]) for l in lines ]
388 defs = [ (a, b.strip("'")) for (a,b) in defs ]
391 def __pif_on_host(self,pif):
392 return self.__pifs.has_key(pif)
394 def __get_pif_records_from_xapi(self, session, host):
396 for (p,rec) in session.xenapi.PIF.get_all_records().items():
397 if rec['host'] != host:
401 self.__pifs[p][f] = rec[f]
402 self.__pifs[p]['other_config'] = {}
403 for f in PIF_OTHERCONFIG_ATTRS:
404 if not rec['other_config'].has_key(f): continue
405 self.__pifs[p]['other_config'][f] = rec['other_config'][f]
407 def __get_vlan_records_from_xapi(self, session):
409 for v in session.xenapi.VLAN.get_all():
410 rec = session.xenapi.VLAN.get_record(v)
411 if not self.__pif_on_host(rec['untagged_PIF']):
415 self.__vlans[v][f] = rec[f]
417 def __get_bond_records_from_xapi(self, session):
419 for b in session.xenapi.Bond.get_all():
420 rec = session.xenapi.Bond.get_record(b)
421 if not self.__pif_on_host(rec['master']):
425 self.__bonds[b][f] = rec[f]
427 def __get_network_records_from_xapi(self, session):
429 for n in session.xenapi.network.get_all():
430 rec = session.xenapi.network.get_record(n)
431 self.__networks[n] = {}
432 for f in NETWORK_ATTRS:
434 # drop PIFs on other hosts
435 self.__networks[n][f] = [p for p in rec[f] if self.__pif_on_host(p)]
437 self.__networks[n][f] = rec[f]
438 self.__networks[n]['other_config'] = {}
439 for f in NETWORK_OTHERCONFIG_ATTRS:
440 if not rec['other_config'].has_key(f): continue
441 self.__networks[n]['other_config'][f] = rec['other_config'][f]
443 def __to_xml(self, xml, parent, key, ref, rec, attrs):
444 """Encode a database object as XML"""
445 e = xml.createElement(key)
446 parent.appendChild(e)
448 e.setAttribute('ref', ref)
450 for n,v in rec.items():
455 raise Error("Unknown attribute %s" % n)
456 def __from_xml(self, e, attrs):
457 """Decode a database object from XML"""
458 ref = e.attributes['ref'].value
460 for n in e.childNodes:
461 if n.nodeName in attrs:
462 _,h = attrs[n.nodeName]
463 rec[n.nodeName] = h(n)
466 def __init__(self, session_ref=None, cache_file=None):
467 if session_ref and cache_file:
468 raise Error("can't specify session reference and cache file")
469 if cache_file == None:
470 session = XenAPI.xapi_local()
473 log("No session ref given on command line, logging in.")
474 session.xenapi.login_with_password("root", "")
476 session._session = session_ref
480 inventory = self.__read_xensource_inventory()
481 assert(inventory.has_key('INSTALLATION_UUID'))
482 log("host uuid is %s" % inventory['INSTALLATION_UUID'])
484 host = session.xenapi.host.get_by_uuid(inventory['INSTALLATION_UUID'])
486 self.__get_pif_records_from_xapi(session, host)
488 self.__get_vlan_records_from_xapi(session)
489 self.__get_bond_records_from_xapi(session)
490 self.__get_network_records_from_xapi(session)
493 session.xenapi.session.logout()
495 log("Loading xapi database cache from %s" % cache_file)
497 xml = parseXML(cache_file)
504 assert(len(xml.childNodes) == 1)
505 toplevel = xml.childNodes[0]
507 assert(toplevel.nodeName == "xenserver-network-configuration")
509 for n in toplevel.childNodes:
510 if n.nodeName == "#text":
512 elif n.nodeName == PIF_XML_TAG:
513 (ref,rec) = self.__from_xml(n, PIF_ATTRS)
514 self.__pifs[ref] = rec
515 elif n.nodeName == BOND_XML_TAG:
516 (ref,rec) = self.__from_xml(n, BOND_ATTRS)
517 self.__bonds[ref] = rec
518 elif n.nodeName == VLAN_XML_TAG:
519 (ref,rec) = self.__from_xml(n, VLAN_ATTRS)
520 self.__vlans[ref] = rec
521 elif n.nodeName == NETWORK_XML_TAG:
522 (ref,rec) = self.__from_xml(n, NETWORK_ATTRS)
523 self.__networks[ref] = rec
525 raise Error("Unknown XML element %s" % n.nodeName)
527 def save(self, cache_file):
529 xml = getDOMImplementation().createDocument(
530 None, "xenserver-network-configuration", None)
531 for (ref,rec) in self.__pifs.items():
532 self.__to_xml(xml, xml.documentElement, PIF_XML_TAG, ref, rec, PIF_ATTRS)
533 for (ref,rec) in self.__bonds.items():
534 self.__to_xml(xml, xml.documentElement, BOND_XML_TAG, ref, rec, BOND_ATTRS)
535 for (ref,rec) in self.__vlans.items():
536 self.__to_xml(xml, xml.documentElement, VLAN_XML_TAG, ref, rec, VLAN_ATTRS)
537 for (ref,rec) in self.__networks.items():
538 self.__to_xml(xml, xml.documentElement, NETWORK_XML_TAG, ref, rec,
541 f = open(cache_file, 'w')
542 f.write(xml.toprettyxml())
545 def get_pif_by_uuid(self, uuid):
546 pifs = map(lambda (ref,rec): ref,
547 filter(lambda (ref,rec): uuid == rec['uuid'],
548 self.__pifs.items()))
550 raise Error("Unknown PIF \"%s\"" % uuid)
552 raise Error("Non-unique PIF \"%s\"" % uuid)
556 def get_pifs_by_device(self, device):
557 return map(lambda (ref,rec): ref,
558 filter(lambda (ref,rec): rec['device'] == device,
559 self.__pifs.items()))
561 def get_pif_by_bridge(self, bridge):
562 networks = map(lambda (ref,rec): ref,
563 filter(lambda (ref,rec): rec['bridge'] == bridge,
564 self.__networks.items()))
565 if len(networks) == 0:
566 raise Error("No matching network \"%s\"" % bridge)
569 for network in networks:
570 nwrec = self.get_network_record(network)
571 for pif in nwrec['PIFs']:
572 pifrec = self.get_pif_record(pif)
574 raise Error("Multiple PIFs on host for network %s" % (bridge))
577 raise Error("No PIF on host for network %s" % (bridge))
580 def get_pif_record(self, pif):
581 if self.__pifs.has_key(pif):
582 return self.__pifs[pif]
583 raise Error("Unknown PIF \"%s\" (get_pif_record)" % pif)
584 def get_all_pifs(self):
586 def pif_exists(self, pif):
587 return self.__pifs.has_key(pif)
589 def get_management_pif(self):
590 """ Returns the management pif on host
592 all = self.get_all_pifs()
594 pifrec = self.get_pif_record(pif)
595 if pifrec['management']: return pif
598 def get_network_record(self, network):
599 if self.__networks.has_key(network):
600 return self.__networks[network]
601 raise Error("Unknown network \"%s\"" % network)
602 def get_all_networks(self):
603 return self.__networks
605 def get_bond_record(self, bond):
606 if self.__bonds.has_key(bond):
607 return self.__bonds[bond]
611 def get_vlan_record(self, vlan):
612 if self.__vlans.has_key(vlan):
613 return self.__vlans[vlan]
618 # Boot from Network filesystem or device.
621 def check_allowed(pif):
622 """Determine whether interface-reconfigure should be manipulating this PIF.
624 Used to prevent system PIFs (such as network root disk) from being interfered with.
627 pifrec = db.get_pif_record(pif)
629 f = open("/proc/ardence")
630 macline = filter(lambda x: x.startswith("HWaddr:"), f.readlines())
632 if len(macline) == 1:
633 p = re.compile(".*\s%(MAC)s\s.*" % pifrec, re.IGNORECASE)
634 if p.match(macline[0]):
635 log("Skipping PVS device %(device)s (%(MAC)s)" % pifrec)
642 # Bare Network Devices -- network devices without IP configuration
645 def netdev_exists(netdev):
646 return os.path.exists("/sys/class/net/" + netdev)
648 def pif_netdev_name(pif):
649 """Get the netdev name for a PIF."""
651 pifrec = db.get_pif_record(pif)
654 return "%(device)s.%(VLAN)s" % pifrec
656 return pifrec['device']
658 def netdev_down(netdev):
659 """Bring down a bare network device"""
662 if not netdev_exists(netdev):
663 log("netdev: down: device %s does not exist, ignoring" % netdev)
665 run_command(["/sbin/ifconfig", netdev, 'down'])
667 def netdev_up(netdev, mtu=None):
668 """Bring up a bare network device"""
671 if not netdev_exists(netdev):
672 raise Error("netdev: up: device %s does not exist" % netdev)
679 run_command(["/sbin/ifconfig", netdev, 'up'] + mtu)
681 def netdev_remap_name(pif, already_renamed=[]):
682 """Check whether 'pif' exists and has the correct MAC.
683 If not, try to find a device with the correct MAC and rename it.
684 'already_renamed' is used to avoid infinite recursion.
690 file = open(name, 'r')
691 return file.readline().rstrip('\n')
696 def get_netdev_mac(device):
698 return read1("/sys/class/net/%s/address" % device)
700 # Probably no such device.
703 def get_netdev_tx_queue_len(device):
705 return int(read1("/sys/class/net/%s/tx_queue_len" % device))
707 # Probably no such device.
710 def get_netdev_by_mac(mac):
711 for device in os.listdir("/sys/class/net"):
712 dev_mac = get_netdev_mac(device)
713 if (dev_mac and mac.lower() == dev_mac.lower() and
714 get_netdev_tx_queue_len(device)):
718 def rename_netdev(old_name, new_name):
719 log("Changing the name of %s to %s" % (old_name, new_name))
720 run_command(['/sbin/ifconfig', old_name, 'down'])
721 if not run_command(['/sbin/ip', 'link', 'set', old_name, 'name', new_name]):
722 raise Error("Could not rename %s to %s" % (old_name, new_name))
724 pifrec = db.get_pif_record(pif)
725 device = pifrec['device']
728 # Is there a network device named 'device' at all?
729 device_exists = netdev_exists(device)
731 # Yes. Does it have MAC 'mac'?
732 found_mac = get_netdev_mac(device)
733 if found_mac and mac.lower() == found_mac.lower():
734 # Yes, everything checks out the way we want. Nothing to do.
737 log("No network device %s" % device)
739 # What device has MAC 'mac'?
740 cur_device = get_netdev_by_mac(mac)
742 log("No network device has MAC %s" % mac)
745 # First rename 'device', if it exists, to get it out of the way
746 # for 'cur_device' to replace it.
748 rename_netdev(device, "dev%d" % random.getrandbits(24))
750 # Rename 'cur_device' to 'device'.
751 rename_netdev(cur_device, device)
754 # IP Network Devices -- network devices with IP configuration
757 def pif_ipdev_name(pif):
758 """Return the ipdev name associated with pif"""
759 pifrec = db.get_pif_record(pif)
760 nwrec = db.get_network_record(pifrec['network'])
763 # TODO: sanity check that nwrec['bridgeless'] != 'true'
764 return nwrec['bridge']
766 # TODO: sanity check that nwrec['bridgeless'] == 'true'
767 return pif_netdev_name(pif)
770 """Bring down a network interface"""
773 if not netdev_exists(netdev):
774 log("ifdown: device %s does not exist, ignoring" % netdev)
776 if not os.path.exists("/etc/sysconfig/network-scripts/ifcfg-%s" % netdev):
777 log("ifdown: device %s exists but ifcfg %s does not" % (netdev,netdev))
779 run_command(["/sbin/ifdown", netdev])
782 """Bring up a network interface"""
785 if not netdev_exists(netdev):
786 raise Error("ifup: device %s does not exist, ignoring" % netdev)
787 if not os.path.exists("/etc/sysconfig/network-scripts/ifcfg-%s" % netdev):
788 raise Error("ifup: device %s exists but ifcfg-%s does not" % (netdev,netdev))
789 run_command(["/sbin/ifup", netdev])
795 def pif_bridge_name(pif):
796 """Return the bridge name of a pif.
798 PIF must not be a VLAN and must be a bridged PIF."""
800 pifrec = db.get_pif_record(pif)
803 raise Error("PIF %(uuid)s cannot be a bridge, VLAN is %(VLAN)s" % pifrec)
805 nwrec = db.get_network_record(pifrec['network'])
808 return nwrec['bridge']
810 raise Error("PIF %(uuid)s does not have a bridge name" % pifrec)
816 def pif_currently_in_use(pif):
817 """Determine if a PIF is currently in use.
819 A PIF is determined to be currently in use if
820 - PIF.currently-attached is true
821 - Any bond master is currently attached
822 - Any VLAN master is currently attached
824 rec = db.get_pif_record(pif)
825 if rec['currently_attached']:
826 log("configure_datapath: %s is currently attached" % (pif_netdev_name(pif)))
828 for b in pif_get_bond_masters(pif):
829 if pif_currently_in_use(b):
830 log("configure_datapath: %s is in use by BOND master %s" % (pif_netdev_name(pif),pif_netdev_name(b)))
832 for v in pif_get_vlan_masters(pif):
833 if pif_currently_in_use(v):
834 log("configure_datapath: %s is in use by VLAN master %s" % (pif_netdev_name(pif),pif_netdev_name(v)))
842 def ethtool_settings(oc):
844 if oc.has_key('ethtool-speed'):
845 val = oc['ethtool-speed']
846 if val in ["10", "100", "1000"]:
847 settings += ['speed', val]
849 log("Invalid value for ethtool-speed = %s. Must be 10|100|1000." % val)
850 if oc.has_key('ethtool-duplex'):
851 val = oc['ethtool-duplex']
852 if val in ["10", "100", "1000"]:
853 settings += ['duplex', 'val']
855 log("Invalid value for ethtool-duplex = %s. Must be half|full." % val)
856 if oc.has_key('ethtool-autoneg'):
857 val = oc['ethtool-autoneg']
858 if val in ["true", "on"]:
859 settings += ['autoneg', 'on']
860 elif val in ["false", "off"]:
861 settings += ['autoneg', 'off']
863 log("Invalid value for ethtool-autoneg = %s. Must be on|true|off|false." % val)
865 for opt in ("rx", "tx", "sg", "tso", "ufo", "gso"):
866 if oc.has_key("ethtool-" + opt):
867 val = oc["ethtool-" + opt]
868 if val in ["true", "on"]:
869 offload += [opt, 'on']
870 elif val in ["false", "off"]:
871 offload += [opt, 'off']
873 log("Invalid value for ethtool-%s = %s. Must be on|true|off|false." % (opt, val))
874 return settings,offload
877 if oc.has_key('mtu'):
879 int(oc['mtu']) # Check that the value is an integer
881 except ValueError, x:
882 log("Invalid value for mtu = %s" % oc['mtu'])
888 def pif_get_bond_masters(pif):
889 """Returns a list of PIFs which are bond masters of this PIF"""
891 pifrec = db.get_pif_record(pif)
893 bso = pifrec['bond_slave_of']
895 # bond-slave-of is currently a single reference but in principle a
896 # PIF could be a member of several bonds which are not
897 # concurrently attached. Be robust to this possibility.
898 if not bso or bso == "OpaqueRef:NULL":
900 elif not type(bso) == list:
903 bondrecs = [db.get_bond_record(bond) for bond in bso]
904 bondrecs = [rec for rec in bondrecs if rec]
906 return [bond['master'] for bond in bondrecs]
908 def pif_get_bond_slaves(pif):
909 """Returns a list of PIFs which make up the given bonded pif."""
911 pifrec = db.get_pif_record(pif)
913 bmo = pifrec['bond_master_of']
915 raise Error("Bond-master-of contains too many elements")
920 bondrec = db.get_bond_record(bmo[0])
922 raise Error("No bond record for bond master PIF")
924 return bondrec['slaves']
930 def pif_is_vlan(pif):
931 return db.get_pif_record(pif)['VLAN'] != '-1'
933 def pif_get_vlan_slave(pif):
934 """Find the PIF which is the VLAN slave of pif.
936 Returns the 'physical' PIF underneath the a VLAN PIF @pif."""
938 pifrec = db.get_pif_record(pif)
940 vlan = pifrec['VLAN_master_of']
941 if not vlan or vlan == "OpaqueRef:NULL":
942 raise Error("PIF is not a VLAN master")
944 vlanrec = db.get_vlan_record(vlan)
946 raise Error("No VLAN record found for PIF")
948 return vlanrec['tagged_PIF']
950 def pif_get_vlan_masters(pif):
951 """Returns a list of PIFs which are VLANs on top of the given pif."""
953 pifrec = db.get_pif_record(pif)
954 vlans = [db.get_vlan_record(v) for v in pifrec['VLAN_slave_of']]
955 return [v['untagged_PIF'] for v in vlans if v and db.pif_exists(v['untagged_PIF'])]
958 # IP device configuration
961 def ipdev_configure_static_routes(interface, oc, f):
962 """Open a route-<interface> file for static routes.
964 Opens the static routes configuration file for interface and writes one
965 line for each route specified in the network's other config "static-routes" value.
967 interface ( RO): xenbr1
968 other-config (MRW): static-routes: 172.16.0.0/15/192.168.0.3,172.18.0.0/16/192.168.0.4;...
970 Then route-xenbr1 should be
971 172.16.0.0/15 via 192.168.0.3 dev xenbr1
972 172.18.0.0/16 via 192.168.0.4 dev xenbr1
974 fname = "route-%s" % interface
975 if oc.has_key('static-routes'):
976 # The key is present - extract comma seperates entries
977 lines = oc['static-routes'].split(',')
979 # The key is not present, i.e. there are no static routes
982 child = ConfigurationFile(fname)
983 child.write("# DO NOT EDIT: This file (%s) was autogenerated by %s\n" % \
984 (os.path.basename(child.path()), os.path.basename(sys.argv[0])))
988 network, masklen, gateway = l.split('/')
989 child.write("%s/%s via %s dev %s\n" % (network, masklen, gateway, interface))
991 f.attach_child(child)
994 except ValueError, e:
995 log("Error in other-config['static-routes'] format for network %s: %s" % (interface, e))
997 def ipdev_open_ifcfg(pif):
998 ipdev = pif_ipdev_name(pif)
1000 log("Writing network configuration for %s" % ipdev)
1002 f = ConfigurationFile("ifcfg-%s" % ipdev)
1004 f.write("# DO NOT EDIT: This file (%s) was autogenerated by %s\n" % \
1005 (os.path.basename(f.path()), os.path.basename(sys.argv[0])))
1006 f.write("XEMANAGED=yes\n")
1007 f.write("DEVICE=%s\n" % ipdev)
1008 f.write("ONBOOT=no\n")
1012 def ipdev_configure_network(pif):
1013 """Write the configuration file for a network.
1015 Writes configuration derived from the network object into the relevant
1016 ifcfg file. The configuration file is passed in, but if the network is
1017 bridgeless it will be ifcfg-<interface>, otherwise it will be ifcfg-<bridge>.
1019 This routine may also write ifcfg files of the networks corresponding to other PIFs
1020 in order to maintain consistency.
1023 pif: Opaque_ref of pif
1024 f : ConfigurationFile(/path/to/ifcfg) to which we append network configuration
1027 pifrec = db.get_pif_record(pif)
1028 nwrec = db.get_network_record(pifrec['network'])
1030 ipdev = pif_ipdev_name(pif)
1032 f = ipdev_open_ifcfg(pif)
1034 mode = pifrec['ip_configuration_mode']
1035 log("Configuring %s using %s configuration" % (ipdev, mode))
1038 if pifrec.has_key('other_config'):
1039 oc = pifrec['other_config']
1041 f.write("TYPE=Ethernet\n")
1042 if pifrec['ip_configuration_mode'] == "DHCP":
1043 f.write("BOOTPROTO=dhcp\n")
1044 f.write("PERSISTENT_DHCLIENT=yes\n")
1045 elif pifrec['ip_configuration_mode'] == "Static":
1046 f.write("BOOTPROTO=none\n")
1047 f.write("NETMASK=%(netmask)s\n" % pifrec)
1048 f.write("IPADDR=%(IP)s\n" % pifrec)
1049 f.write("GATEWAY=%(gateway)s\n" % pifrec)
1050 elif pifrec['ip_configuration_mode'] == "None":
1051 f.write("BOOTPROTO=none\n")
1053 raise Error("Unknown ip-configuration-mode %s" % pifrec['ip_configuration_mode'])
1055 if nwrec.has_key('other_config'):
1056 settings,offload = ethtool_settings(nwrec['other_config'])
1058 f.write("ETHTOOL_OPTS=\"%s\"\n" % str.join(" ", settings))
1060 f.write("ETHTOOL_OFFLOAD_OPTS=\"%s\"\n" % str.join(" ", offload))
1062 mtu = mtu_setting(nwrec['other_config'])
1064 f.write("MTU=%s\n" % mtu)
1066 ipdev_configure_static_routes(ipdev, nwrec['other_config'], f)
1068 if pifrec.has_key('DNS') and pifrec['DNS'] != "":
1069 ServerList = pifrec['DNS'].split(",")
1070 for i in range(len(ServerList)): f.write("DNS%d=%s\n" % (i+1, ServerList[i]))
1071 if oc and oc.has_key('domain'):
1072 f.write("DOMAIN='%s'\n" % oc['domain'].replace(',', ' '))
1074 # We only allow one ifcfg-* to have PEERDNS=yes and there can be
1075 # only one GATEWAYDEV in /etc/sysconfig/network.
1077 # The peerdns pif will be the one with
1078 # pif::other-config:peerdns=true, or the mgmt pif if none have
1081 # The gateway pif will be the one with
1082 # pif::other-config:defaultroute=true, or the mgmt pif if none
1085 # Work out which pif on this host should be the one with
1086 # PEERDNS=yes and which should be the GATEWAYDEV
1088 # Note: we prune out the bond master pif (if it exists). This is
1089 # because when we are called to bring up an interface with a bond
1090 # master, it is implicit that we should bring down that master.
1091 pifs_on_host = [ __pif for __pif in db.get_all_pifs() if
1092 not __pif in pif_get_bond_masters(pif) ]
1093 other_pifs_on_host = [ __pif for __pif in pifs_on_host if __pif != pif ]
1096 defaultroute_pif = None
1098 # loop through all the pifs on this host looking for one with
1099 # other-config:peerdns = true, and one with
1100 # other-config:default-route=true
1101 for __pif in pifs_on_host:
1102 __pifrec = db.get_pif_record(__pif)
1103 __oc = __pifrec['other_config']
1104 if __oc.has_key('peerdns') and __oc['peerdns'] == 'true':
1105 if peerdns_pif == None:
1108 log('Warning: multiple pifs with "peerdns=true" - choosing %s and ignoring %s' % \
1109 (db.get_pif_record(peerdns_pif)['device'], __pifrec['device']))
1110 if __oc.has_key('defaultroute') and __oc['defaultroute'] == 'true':
1111 if defaultroute_pif == None:
1112 defaultroute_pif = __pif
1114 log('Warning: multiple pifs with "defaultroute=true" - choosing %s and ignoring %s' % \
1115 (db.get_pif_record(defaultroute_pif)['device'], __pifrec['device']))
1117 # If no pif is explicitly specified then use the mgmt pif for
1118 # peerdns/defaultroute.
1119 if peerdns_pif == None:
1120 peerdns_pif = management_pif
1121 if defaultroute_pif == None:
1122 defaultroute_pif = management_pif
1124 # Update all the other network's ifcfg files and ensure
1126 for __pif in other_pifs_on_host:
1127 __f = ipdev_open_ifcfg(__pif)
1128 peerdns_line_wanted = 'PEERDNS=%s\n' % ((__pif == peerdns_pif) and 'yes' or 'no')
1129 lines = __f.readlines()
1131 if not peerdns_line_wanted in lines:
1132 # the PIF selected for DNS has changed and as a result this ifcfg file needs rewriting
1134 if not line.lstrip().startswith('PEERDNS'):
1136 log("Setting %s in %s" % (peerdns_line_wanted.strip(), __f.path()))
1137 __f.write(peerdns_line_wanted)
1142 # There is no need to change this ifcfg file. So don't attach_child.
1145 # ... and for this pif too
1146 f.write('PEERDNS=%s\n' % ((pif == peerdns_pif) and 'yes' or 'no'))
1149 fnetwork = ConfigurationFile("network", "/etc/sysconfig")
1150 for line in fnetwork.readlines():
1151 if line.lstrip().startswith('GATEWAY') :
1153 fnetwork.write(line)
1154 if defaultroute_pif:
1155 gatewaydev = pif_ipdev_name(defaultroute_pif)
1157 gatewaydev = pif_netdev_name(defaultroute_pif)
1158 fnetwork.write('GATEWAYDEV=%s\n' % gatewaydev)
1160 f.attach_child(fnetwork)
1165 # Datapath Configuration
1168 def pif_datapath(pif):
1169 """Return the OpenFlow datapath name associated with pif.
1170 For a non-VLAN PIF, the datapath name is the bridge name.
1171 For a VLAN PIF, the datapath name is the bridge name for the PIF's VLAN slave.
1173 if pif_is_vlan(pif):
1174 return pif_datapath(pif_get_vlan_slave(pif))
1176 pifrec = db.get_pif_record(pif)
1177 nwrec = db.get_network_record(pifrec['network'])
1178 if not nwrec['bridge']:
1179 raise Error("datapath PIF cannot be bridgeless")
1183 def datapath_get_physical_pifs(pif):
1184 """Return the PIFs for the physical network device(s) associated with a datapath PIF.
1185 For a bond master PIF, these are the bond slave PIFs.
1186 For a non-VLAN, non-bond master PIF, the PIF is its own physical device PIF.
1188 A VLAN PIF cannot be a datapath PIF.
1190 pifrec = db.get_pif_record(pif)
1192 if pif_is_vlan(pif):
1193 raise Error("get-physical-pifs should not get passed a VLAN")
1194 elif len(pifrec['bond_master_of']) != 0:
1195 return pif_get_bond_slaves(pif)
1199 def datapath_deconfigure_physical(netdev):
1200 # The use of [!0-9] keeps an interface of 'eth0' from matching
1201 # VLANs attached to eth0 (such as 'eth0.123'), which are distinct
1203 return ['--del-match=bridge.*.port=%s' % netdev,
1204 '--del-match=port.%s.[!0-9]*' % netdev,
1205 '--del-match=bonding.*.slave=%s' % netdev,
1206 '--del-match=iface.%s.[!0-9]*' % netdev]
1208 def datapath_configure_bond(pif,slaves):
1209 pifrec = db.get_pif_record(pif)
1210 interface = pif_netdev_name(pif)
1212 argv = ['--del-match=bonding.%s.[!0-9]*' % interface]
1213 argv += ["--add=bonding.%s.slave=%s" % (interface, pif_netdev_name(slave))
1214 for slave in slaves]
1215 argv += ['--add=bonding.%s.fake-iface=true' % interface]
1217 if pifrec['MAC'] != "":
1218 argv += ['--add=port.%s.mac=%s' % (interface, pifrec['MAC'])]
1222 "mode": "balance-slb",
1228 # override defaults with values from other-config whose keys
1229 # being with "bond-"
1230 oc = pifrec['other_config']
1231 overrides = filter(lambda (key,val):
1232 key.startswith("bond-"), oc.items())
1233 overrides = map(lambda (key,val): (key[5:], val), overrides)
1234 bond_options.update(overrides)
1235 for (name,val) in bond_options.items():
1236 argv += ["--add=bonding.%s.%s=%s" % (interface, name, val)]
1239 def datapath_deconfigure_bond(netdev):
1240 # The use of [!0-9] keeps an interface of 'eth0' from matching
1241 # VLANs attached to eth0 (such as 'eth0.123'), which are distinct
1243 return ['--del-match=bonding.%s.[!0-9]*' % netdev,
1244 '--del-match=port.%s.[!0-9]*' % netdev]
1246 def datapath_deconfigure_ipdev(interface):
1247 # The use of [!0-9] keeps an interface of 'eth0' from matching
1248 # VLANs attached to eth0 (such as 'eth0.123'), which are distinct
1250 return ['--del-match=bridge.*.port=%s' % interface,
1251 '--del-match=port.%s.[!0-9]*' % interface,
1252 '--del-match=iface.%s.[!0-9]*' % interface,
1253 '--del-match=vlan.%s.[!0-9]*' % interface]
1255 def datapath_modify_config(commands):
1257 log("modifying configuration:")
1261 rc = run_command(['/usr/bin/ovs-cfg-mod', '-vANY:console:emer',
1262 '-F', '/etc/ovs-vswitchd.conf']
1263 + [c for c in commands if c[0] != '#'] + ['-c'])
1265 raise Error("Failed to modify vswitch configuration")
1266 run_command(['/sbin/service', 'vswitch', 'reload'])
1270 # Toplevel Datapath Configuration.
1273 def configure_datapath(pif):
1274 """Bring up the datapath configuration for PIF.
1276 Should be careful not to glitch existing users of the datapath, e.g. other VLANs etc.
1278 Should take care of tearing down other PIFs which encompass common physical devices.
1280 Returns a tuple containing
1281 - A list containing the necessary cfgmod command line arguments
1282 - A list of additional devices which should be brought up after
1283 the configuration is applied.
1289 bridge = pif_bridge_name(pif)
1291 physical_devices = datapath_get_physical_pifs(pif)
1293 # Determine additional devices to deconfigure.
1295 # Given all physical devices which are part of this PIF we need to
1297 # - any additional bond which a physical device is part of.
1298 # - any additional physical devices which are part of an additional bond.
1300 # Any of these which are not currently in use should be brought
1301 # down and deconfigured.
1302 extra_down_bonds = []
1303 extra_down_ports = []
1304 for p in physical_devices:
1305 for bond in pif_get_bond_masters(p):
1307 log("configure_datapath: leaving bond %s up" % pif_netdev_name(bond))
1309 if bond in extra_down_bonds:
1311 if db.get_pif_record(bond)['currently_attached']:
1312 log("configure_datapath: implicitly tearing down currently-attached bond %s" % pif_netdev_name(bond))
1314 extra_down_bonds += [bond]
1316 for s in pif_get_bond_slaves(bond):
1317 if s in physical_devices:
1319 if s in extra_down_ports:
1321 if pif_currently_in_use(s):
1323 extra_down_ports += [s]
1325 log("configure_datapath: bridge - %s" % bridge)
1326 log("configure_datapath: physical - %s" % [pif_netdev_name(p) for p in physical_devices])
1327 log("configure_datapath: extra ports - %s" % [pif_netdev_name(p) for p in extra_down_ports])
1328 log("configure_datapath: extra bonds - %s" % [pif_netdev_name(p) for p in extra_down_bonds])
1330 # Need to fully deconfigure any bridge which any of the:
1331 # - physical devices
1335 for brpif in physical_devices + extra_down_ports + extra_down_bonds:
1338 b = pif_bridge_name(brpif)
1340 cfgmod_argv += ['# remove bridge %s' % b]
1341 cfgmod_argv += ['--del-match=bridge.%s.*' % b]
1343 for n in extra_down_ports:
1344 dev = pif_netdev_name(n)
1345 cfgmod_argv += ['# deconfigure sibling physical device %s' % dev]
1346 cfgmod_argv += datapath_deconfigure_physical(dev)
1349 for n in extra_down_bonds:
1350 dev = pif_netdev_name(n)
1351 cfgmod_argv += ['# deconfigure bond device %s' % dev]
1352 cfgmod_argv += datapath_deconfigure_bond(dev)
1355 for p in physical_devices:
1356 dev = pif_netdev_name(p)
1357 cfgmod_argv += ['# deconfigure physical port %s' % dev]
1358 cfgmod_argv += datapath_deconfigure_physical(dev)
1360 # Check the MAC address of each network device and remap if
1361 # necessary to make names match our expectations.
1362 for p in physical_devices:
1363 netdev_remap_name(p)
1365 # Bring up physical devices early, because ovs-vswitchd initially
1366 # enables or disables bond slaves based on whether carrier is
1367 # detected when they are added, and a network device that is down
1368 # always reports "no carrier".
1369 for p in physical_devices:
1370 oc = db.get_pif_record(p)['other_config']
1372 dev = pif_netdev_name(p)
1374 mtu = mtu_setting(oc)
1378 settings, offload = ethtool_settings(oc)
1380 run_command(['/sbin/ethtool', '-s', dev] + settings)
1382 run_command(['/sbin/ethtool', '-K', dev] + offload)
1384 if len(physical_devices) > 1:
1385 cfgmod_argv += ['# deconfigure bond %s' % pif_netdev_name(pif)]
1386 cfgmod_argv += datapath_deconfigure_bond(pif_netdev_name(pif))
1387 cfgmod_argv += ['--del-entry=bridge.%s.port=%s' % (bridge,pif_netdev_name(pif))]
1388 cfgmod_argv += ['# configure bond %s' % pif_netdev_name(pif)]
1389 cfgmod_argv += datapath_configure_bond(pif, physical_devices)
1390 cfgmod_argv += ['--add=bridge.%s.port=%s' % (bridge,pif_netdev_name(pif)) ]
1391 extra_up_ports += [pif_netdev_name(pif)]
1393 iface = pif_netdev_name(physical_devices[0])
1394 cfgmod_argv += ['# add physical device %s' % iface]
1395 cfgmod_argv += ['--add=bridge.%s.port=%s' % (bridge,iface) ]
1397 return cfgmod_argv,extra_up_ports
1399 def deconfigure_datapath(pif):
1402 bridge = pif_bridge_name(pif)
1404 physical_devices = datapath_get_physical_pifs(pif)
1406 log("deconfigure_datapath: bridge - %s" % bridge)
1407 log("deconfigure_datapath: physical devices - %s" % [pif_netdev_name(p) for p in physical_devices])
1409 for p in physical_devices:
1410 dev = pif_netdev_name(p)
1411 cfgmod_argv += ['# deconfigure physical port %s' % dev]
1412 cfgmod_argv += datapath_deconfigure_physical(dev)
1415 if len(physical_devices) > 1:
1416 cfgmod_argv += ['# deconfigure bond %s' % pif_netdev_name(pif)]
1417 cfgmod_argv += datapath_deconfigure_bond(pif_netdev_name(pif))
1419 cfgmod_argv += ['# deconfigure bridge %s' % bridge]
1420 cfgmod_argv += ['--del-match=bridge.%s.*' % bridge]
1429 pifrec = db.get_pif_record(pif)
1433 ipdev = pif_ipdev_name(pif)
1434 dp = pif_datapath(pif)
1435 bridge = pif_bridge_name(dp)
1437 log("action_up: %s on bridge %s" % (ipdev, bridge))
1442 c,e = configure_datapath(dp)
1446 cfgmod_argv += ['# configure xs-network-uuids']
1447 cfgmod_argv += ['--del-match=bridge.%s.xs-network-uuids=*' % bridge]
1449 for nwpif in db.get_pifs_by_device(db.get_pif_record(pif)['device']):
1450 rec = db.get_pif_record(nwpif)
1452 # When state is read from dbcache PIF.currently_attached
1453 # is always assumed to be false... Err on the side of
1454 # listing even detached networks for the time being.
1455 #if nwpif != pif and not rec['currently_attached']:
1456 # log("Network PIF %s not currently attached (%s)" % (rec['uuid'],pifrec['uuid']))
1458 nwrec = db.get_network_record(rec['network'])
1459 cfgmod_argv += ['--add=bridge.%s.xs-network-uuids=%s' % (bridge, nwrec['uuid'])]
1461 cfgmod_argv += ["# deconfigure ipdev %s" % ipdev]
1462 cfgmod_argv += datapath_deconfigure_ipdev(ipdev)
1463 cfgmod_argv += ["# reconfigure ipdev %s" % ipdev]
1464 cfgmod_argv += ['--add=bridge.%s.port=%s' % (bridge, ipdev)]
1466 f = ipdev_configure_network(pif)
1469 # /etc/xensource/scripts/vif needs to know where to add VIFs.
1470 if pif_is_vlan(pif):
1472 raise Error("Unbridged VLAN devices not implemented yet")
1473 cfgmod_argv += ['--add=vlan.%s.tag=%s' % (ipdev, pifrec['VLAN'])]
1474 cfgmod_argv += ['--add=iface.%s.internal=true' % (ipdev)]
1475 cfgmod_argv += ['--add=iface.%s.fake-bridge=true' % (ipdev)]
1477 # Apply updated configuration.
1481 datapath_modify_config(cfgmod_argv)
1485 for p in extra_ports:
1488 # Update /etc/issue (which contains the IP address of the management interface)
1489 os.system("/sbin/update-issue")
1493 log("failed to apply changes: %s" % e.msg)
1497 def action_down(pif):
1498 pifrec = db.get_pif_record(pif)
1501 ipdev = pif_ipdev_name(pif)
1502 dp = pif_datapath(pif)
1503 bridge = pif_bridge_name(dp)
1505 log("action_down: %s on bridge %s" % (ipdev, bridge))
1510 nw = db.get_pif_record(pif)['network']
1511 nwrec = db.get_network_record(nw)
1512 cfgmod_argv += ['# deconfigure xs-network-uuids']
1513 cfgmod_argv += ['--del-entry=bridge.%s.xs-network-uuids=%s' % (bridge,nwrec['uuid'])]
1515 log("deconfigure ipdev %s on %s" % (ipdev,bridge))
1516 cfgmod_argv += ["# deconfigure ipdev %s" % ipdev]
1517 cfgmod_argv += datapath_deconfigure_ipdev(ipdev)
1519 f = ipdev_open_ifcfg(pif)
1522 if pif_is_vlan(pif):
1523 br = ConfigurationFile("br-%s" % bridge, vswitch_state_dir)
1527 # If the VLAN's slave is attached, leave datapath setup.
1528 slave = pif_get_vlan_slave(pif)
1529 if db.get_pif_record(slave)['currently_attached']:
1530 log("action_down: vlan slave is currently attached")
1533 # If the VLAN's slave has other VLANs that are attached, leave datapath setup.
1534 for master in pif_get_vlan_masters(slave):
1535 if master != pif and db.get_pif_record(master)['currently_attached']:
1536 log("action_down: vlan slave has other master: %s" % pif_netdev_name(master))
1539 # Otherwise, take down the datapath too (fall through)
1541 log("action_down: no more masters, bring down slave %s" % bridge)
1543 # Stop here if this PIF has attached VLAN masters.
1544 masters = [db.get_pif_record(m)['VLAN'] for m in pif_get_vlan_masters(pif) if db.get_pif_record(m)['currently_attached']]
1545 if len(masters) > 0:
1546 log("Leaving datapath %s up due to currently attached VLAN masters %s" % (bridge, masters))
1550 cfgmod_argv += deconfigure_datapath(dp)
1555 datapath_modify_config(cfgmod_argv)
1559 log("action_down failed to apply changes: %s" % e.msg)
1563 def action_rewrite(pif):
1564 f = ipdev_configure_network(pif)
1570 log("failed to apply changes: %s" % e.msg)
1574 def action_force_rewrite(bridge, config):
1575 raise Error("Force rewrite is not implemented yet.")
1577 def main(argv=None):
1578 global output_directory, management_pif
1584 force_interface = None
1585 force_management = False
1593 longops = [ "output-directory=",
1594 "pif=", "pif-uuid=",
1599 "device=", "mode=", "ip=", "netmask=", "gateway=",
1601 arglist, args = getopt.gnu_getopt(argv[1:], shortops, longops)
1602 except getopt.GetoptError, msg:
1605 force_rewrite_config = {}
1608 if o == "--output-directory":
1609 output_directory = a
1612 elif o == "--pif-uuid":
1614 elif o == "--session":
1616 elif o == "--force-interface" or o == "--force":
1618 elif o == "--management":
1619 force_management = True
1620 elif o in ["--device", "--mode", "--ip", "--netmask", "--gateway"]:
1621 force_rewrite_config[o[2:]] = a
1622 elif o == "-h" or o == "--help":
1623 print __doc__ % {'command-name': os.path.basename(argv[0])}
1626 if not debug_mode():
1627 syslog.openlog(os.path.basename(argv[0]))
1628 log("Called as " + str.join(" ", argv))
1630 raise Usage("Required option <action> not present")
1632 raise Usage("Too many arguments")
1636 if not action in ["up", "down", "rewrite", "rewrite-configuration"]:
1637 raise Usage("Unknown action \"%s\"" % action)
1639 # backwards compatibility
1640 if action == "rewrite-configuration": action = "rewrite"
1642 if output_directory and ( session or pif ):
1643 raise Usage("--session/--pif cannot be used with --output-directory")
1644 if ( session or pif ) and pif_uuid:
1645 raise Usage("--session/--pif and --pif-uuid are mutually exclusive.")
1646 if ( session and not pif ) or ( not session and pif ):
1647 raise Usage("--session and --pif must be used together.")
1648 if force_interface and ( session or pif or pif_uuid ):
1649 raise Usage("--force is mutually exclusive with --session, --pif and --pif-uuid")
1650 if force_interface == "all" and action != "down":
1651 raise Usage("\"--force all\" only valid for down action")
1652 if len(force_rewrite_config) and not (force_interface and action == "rewrite"):
1653 raise Usage("\"--force rewrite\" needed for --device, --mode, --ip, --netmask, and --gateway")
1657 log("Force interface %s %s" % (force_interface, action))
1659 if action == "rewrite":
1660 action_force_rewrite(force_interface, force_rewrite_config)
1661 elif action in ["up", "down"]:
1662 if action == "down" and force_interface == "all":
1663 raise Error("Force all interfaces down not implemented yet")
1665 db = DatabaseCache(cache_file=dbcache_file)
1666 pif = db.get_pif_by_bridge(force_interface)
1667 management_pif = db.get_management_pif()
1671 elif action == "down":
1674 raise Error("Unknown action %s" % action)
1676 db = DatabaseCache(session_ref=session)
1679 pif = db.get_pif_by_uuid(pif_uuid)
1681 if action == "rewrite" and not pif:
1685 raise Usage("No PIF given")
1687 if force_management:
1688 # pif is going to be the management pif
1689 management_pif = pif
1691 # pif is not going to be the management pif.
1692 # Search DB cache for pif on same host with management=true
1693 pifrec = db.get_pif_record(pif)
1694 management_pif = db.get_management_pif()
1696 log_pif_action(action, pif)
1698 if not check_allowed(pif):
1703 elif action == "down":
1705 elif action == "rewrite":
1708 raise Error("Unknown action %s" % action)
1711 db.save(dbcache_file)
1714 print >>sys.stderr, err.msg
1715 print >>sys.stderr, "For help use --help."
1723 if __name__ == "__main__":
1729 err = traceback.format_exception(*ex)
1733 if not debug_mode():