3 # Copyright (c) Citrix Systems 2008. All rights reserved.
4 # Copyright (c) 2009 Nicira Networks.
8 %(command-name)s --session <SESSION-REF> --pif <PIF-REF> [up|down|rewrite]
9 %(command-name)s --force <BRIDGE> [up|down|rewrite <CONFIG>]
10 %(command-name)s --force all down
13 <CONFIG> = --device=<INTERFACE> --mode=dhcp
14 <CONFIG> = --device=<INTERFACE> --mode=static --ip=<IPADDR> --netmask=<NM> [--gateway=<GW>]
17 --session A session reference to use to access the xapi DB
18 --pif A PIF reference.
19 --force-interface An interface name. Mutually exclusive with --session/--pif.
21 Either both --session and --pif or just --pif-uuid.
23 <ACTION> is either "up" or "down" or "rewrite"
27 # Undocumented parameters for test & dev:
29 # --output-directory=<DIR> Write configuration to <DIR>. Also disables actually
30 # raising/lowering the interfaces
31 # --pif-uuid A PIF UUID, use instead of --session/--pif.
36 # 1. Every pif belongs to exactly one network
37 # 2. Every network has zero or one pifs
38 # 3. A network may have an associated bridge, allowing vifs to be attached
39 # 4. A network may be bridgeless (there's no point having a bridge over a storage pif)
41 # XXX: --force-interface=all down
43 # XXX: --force-interface rewrite
45 # XXX: Sometimes this leaves "orphaned" datapaths, e.g. a datapath whose
46 # only port is the local port. Should delete those.
48 # XXX: This can leave crud in ovs-vswitchd.conf in this scenario:
49 # - Create bond in XenCenter.
50 # - Create VLAN on bond in XenCenter.
51 # - Attempt to delete bond in XenCenter (this will fail because there
52 # is a VLAN on the bond, although the error may not be reported
53 # until the next step)
54 # - Delete VLAN in XenCenter.
55 # - Delete bond in XenCenter.
56 # At this point there will still be some configuration data for the bond
57 # or the VLAN in ovs-vswitchd.conf.
60 import os, sys, getopt, time, signal
67 output_directory = None
72 dbcache_file = "/etc/ovs-vswitch.dbcache"
73 vswitch_config_dir = "/etc/openvswitch"
75 class Usage(Exception):
76 def __init__(self, msg):
77 Exception.__init__(self)
80 class Error(Exception):
81 def __init__(self, msg):
82 Exception.__init__(self)
85 class ConfigurationFile(object):
86 """Write a file, tracking old and new versions.
88 Supports writing a new version of a file and applying and
89 reverting those changes.
92 __STATE = {"OPEN":"OPEN",
93 "NOT-APPLIED":"NOT-APPLIED", "APPLIED":"APPLIED",
94 "REVERTED":"REVERTED", "COMMITTED": "COMMITTED"}
96 def __init__(self, fname, path="/etc/sysconfig/network-scripts"):
98 self.__state = self.__STATE['OPEN']
103 dirname = output_directory
107 self.__path = os.path.join(dirname, fname)
108 self.__oldpath = os.path.join(dirname, "." + fname + ".xapi-old")
109 self.__newpath = os.path.join(dirname, "." + fname + ".xapi-new")
110 self.__unlink = False
112 self.__f = open(self.__newpath, "w")
114 def attach_child(self, child):
115 self.__children.append(child)
122 return open(self.path()).readlines()
126 def write(self, args):
127 if self.__state != self.__STATE['OPEN']:
128 raise Error("Attempt to write to file in state %s" % self.__state)
132 if self.__state != self.__STATE['OPEN']:
133 raise Error("Attempt to unlink file in state %s" % self.__state)
136 self.__state = self.__STATE['NOT-APPLIED']
139 if self.__state != self.__STATE['OPEN']:
140 raise Error("Attempt to close file in state %s" % self.__state)
143 self.__state = self.__STATE['NOT-APPLIED']
146 if self.__state != self.__STATE['NOT-APPLIED']:
147 raise Error("Attempt to compare file in state %s" % self.__state)
152 if self.__state != self.__STATE['NOT-APPLIED']:
153 raise Error("Attempt to apply configuration from state %s" % self.__state)
155 for child in self.__children:
158 log("Applying changes to %s configuration" % self.__fname)
160 # Remove previous backup.
161 if os.access(self.__oldpath, os.F_OK):
162 os.unlink(self.__oldpath)
164 # Save current configuration.
165 if os.access(self.__path, os.F_OK):
166 os.link(self.__path, self.__oldpath)
167 os.unlink(self.__path)
169 # Apply new configuration.
170 assert(os.path.exists(self.__newpath))
171 if not self.__unlink:
172 os.link(self.__newpath, self.__path)
174 pass # implicit unlink of original file
176 # Remove temporary file.
177 os.unlink(self.__newpath)
179 self.__state = self.__STATE['APPLIED']
182 if self.__state != self.__STATE['APPLIED']:
183 raise Error("Attempt to revert configuration from state %s" % self.__state)
185 for child in self.__children:
188 log("Reverting changes to %s configuration" % self.__fname)
190 # Remove existing new configuration
191 if os.access(self.__newpath, os.F_OK):
192 os.unlink(self.__newpath)
194 # Revert new configuration.
195 if os.access(self.__path, os.F_OK):
196 os.link(self.__path, self.__newpath)
197 os.unlink(self.__path)
199 # Revert to old configuration.
200 if os.access(self.__oldpath, os.F_OK):
201 os.link(self.__oldpath, self.__path)
202 os.unlink(self.__oldpath)
204 # Leave .*.xapi-new as an aid to debugging.
206 self.__state = self.__STATE['REVERTED']
209 if self.__state != self.__STATE['APPLIED']:
210 raise Error("Attempt to commit configuration from state %s" % self.__state)
212 for child in self.__children:
215 log("Committing changes to %s configuration" % self.__fname)
217 if os.access(self.__oldpath, os.F_OK):
218 os.unlink(self.__oldpath)
219 if os.access(self.__newpath, os.F_OK):
220 os.unlink(self.__newpath)
222 self.__state = self.__STATE['COMMITTED']
225 return output_directory is not None
229 print >>sys.stderr, s
233 def check_allowed(pif):
234 pifrec = db.get_pif_record(pif)
236 f = open("/proc/ardence")
237 macline = filter(lambda x: x.startswith("HWaddr:"), f.readlines())
239 if len(macline) == 1:
240 p = re.compile(".*\s%(MAC)s\s.*" % pifrec, re.IGNORECASE)
241 if p.match(macline[0]):
242 log("Skipping PVS device %(device)s (%(MAC)s)" % pifrec)
248 def interface_exists(i):
249 return os.path.exists("/sys/class/net/" + i)
251 ETHTOOL_OTHERCONFIG_ATTRS = ['ethtool-%s' % x for x in 'autoneg', 'speed', 'duplex', 'rx', 'tx', 'sg', 'tso', 'ufo', 'gso' ]
253 PIF_ATTRS = [ 'uuid',
262 'ip_configuration_mode',
270 PIF_OTHERCONFIG_ATTRS = [ 'domain', 'peerdns', 'defaultroute', 'mtu', 'static-routes' ] + \
271 [ 'bond-%s' % x for x in 'mode', 'miimon', 'downdelay', 'updelay', 'use_carrier' ] + \
272 ETHTOOL_OTHERCONFIG_ATTRS
274 VLAN_ATTRS = [ 'uuid',
279 BOND_ATTRS = [ 'uuid',
284 NETWORK_ATTRS = [ 'uuid',
289 NETWORK_OTHERCONFIG_ATTRS = [ 'mtu', 'static-routes' ] + ETHTOOL_OTHERCONFIG_ATTRS
292 class DatabaseCache(object):
293 def __read_xensource_inventory(self):
294 filename = "/etc/xensource-inventory"
295 f = open(filename, "r")
296 lines = [x.strip("\n") for x in f.readlines()]
299 defs = [ (l[:l.find("=")], l[(l.find("=") + 1):]) for l in lines ]
300 defs = [ (a, b.strip("'")) for (a,b) in defs ]
305 def __pif_on_host(self,pif):
306 return self.__pifs.has_key(pif)
309 def __get_pif_records_from_xapi(self, session, host):
311 for (p,rec) in session.xenapi.PIF.get_all_records().items():
312 if rec['host'] != host:
316 self.__pifs[p][f] = rec[f]
317 self.__pifs[p]['other_config'] = {}
318 for f in PIF_OTHERCONFIG_ATTRS:
319 if not rec['other_config'].has_key(f): continue
320 self.__pifs[p]['other_config'][f] = rec['other_config'][f]
322 def __get_vlan_records_from_xapi(self, session):
324 for v in session.xenapi.VLAN.get_all():
325 rec = session.xenapi.VLAN.get_record(v)
326 if not self.__pif_on_host(rec['untagged_PIF']):
330 self.__vlans[v][f] = rec[f]
332 def __get_bond_records_from_xapi(self, session):
334 for b in session.xenapi.Bond.get_all():
335 rec = session.xenapi.Bond.get_record(b)
336 if not self.__pif_on_host(rec['master']):
340 self.__bonds[b][f] = rec[f]
342 def __get_network_records_from_xapi(self, session):
344 for n in session.xenapi.network.get_all():
345 rec = session.xenapi.network.get_record(n)
346 self.__networks[n] = {}
347 for f in NETWORK_ATTRS:
348 self.__networks[n][f] = rec[f]
349 self.__networks[n]['other_config'] = {}
350 for f in NETWORK_OTHERCONFIG_ATTRS:
351 if not rec['other_config'].has_key(f): continue
352 self.__networks[n]['other_config'][f] = rec['other_config'][f]
354 def __init__(self, session_ref=None, cache_file=None):
355 if session_ref and cache_file:
356 raise Error("can't specify session reference and cache file")
357 if cache_file == None:
358 session = XenAPI.xapi_local()
361 log("No session ref given on command line, logging in.")
362 session.xenapi.login_with_password("root", "")
364 session._session = session_ref
368 inventory = self.__read_xensource_inventory()
369 assert(inventory.has_key('INSTALLATION_UUID'))
370 log("host uuid is %s" % inventory['INSTALLATION_UUID'])
372 host = session.xenapi.host.get_by_uuid(inventory['INSTALLATION_UUID'])
374 self.__get_pif_records_from_xapi(session, host)
376 self.__get_vlan_records_from_xapi(session)
377 self.__get_bond_records_from_xapi(session)
378 self.__get_network_records_from_xapi(session)
381 session.xenapi.session.logout()
383 log("Loading xapi database cache from %s" % cache_file)
384 f = open(cache_file, 'r')
385 members = pickle.load(f)
388 self.__vlans = members['vlans']
389 self.__bonds = members['bonds']
390 self.__pifs = members['pifs']
391 self.__networks = members['networks']
393 def save(self, cache_file):
394 f = open(cache_file, 'w')
395 pickle.dump({'vlans': self.__vlans,
396 'bonds': self.__bonds,
398 'networks': self.__networks}, f)
401 def get_pif_by_uuid(self, uuid):
402 pifs = map(lambda (ref,rec): ref,
403 filter(lambda (ref,rec): uuid == rec['uuid'],
404 self.__pifs.items()))
406 raise Error("Unknown PIF \"%s\"" % uuid)
408 raise Error("Non-unique PIF \"%s\"" % uuid)
412 def get_pifs_by_device(self, device):
413 return map(lambda (ref,rec): ref,
414 filter(lambda (ref,rec): rec['device'] == device,
415 self.__pifs.items()))
417 def get_pif_by_bridge(self, bridge):
418 networks = map(lambda (ref,rec): ref,
419 filter(lambda (ref,rec): rec['bridge'] == bridge,
420 self.__networks.items()))
421 if len(networks) == 0:
422 raise Error("No matching network \"%s\"")
425 for network in networks:
426 nwrec = self.get_network_record(network)
427 for pif in nwrec['PIFs']:
428 pifrec = self.get_pif_record(pif)
430 raise Error("Multiple PIFs on host for network %s" % (bridge))
433 raise Error("No PIF on host for network %s" % (bridge))
436 def get_pif_record(self, pif):
437 if self.__pifs.has_key(pif):
438 return self.__pifs[pif]
439 raise Error("Unknown PIF \"%s\"" % pif)
440 def get_all_pifs(self):
442 def pif_exists(self, pif):
443 return self.__pifs.has_key(pif)
445 def get_management_pif(self):
446 """ Returns the management pif on host
448 all = self.get_all_pifs()
450 pifrec = self.get_pif_record(pif)
451 if pifrec['management']: return pif
454 def get_network_record(self, network):
455 if self.__networks.has_key(network):
456 return self.__networks[network]
457 raise Error("Unknown network \"%s\"" % network)
458 def get_all_networks(self):
459 return self.__networks
461 def get_bond_record(self, bond):
462 if self.__bonds.has_key(bond):
463 return self.__bonds[bond]
467 def get_vlan_record(self, vlan):
468 if self.__vlans.has_key(vlan):
469 return self.__vlans[vlan]
473 def bridge_name(pif):
474 """Return the bridge name associated with pif, or None if network is bridgeless"""
475 pifrec = db.get_pif_record(pif)
476 nwrec = db.get_network_record(pifrec['network'])
479 # TODO: sanity check that nwrec['bridgeless'] != 'true'
480 return nwrec['bridge']
482 # TODO: sanity check that nwrec['bridgeless'] == 'true'
485 def interface_name(pif):
486 """Construct an interface name from the given PIF record."""
488 pifrec = db.get_pif_record(pif)
490 if pifrec['VLAN'] == '-1':
491 return pifrec['device']
493 return "%(device)s.%(VLAN)s" % pifrec
495 def datapath_name(pif):
496 """Return the OpenFlow datapath name associated with pif.
497 For a non-VLAN PIF, the datapath name is the bridge name.
498 For a VLAN PIF, the datapath name is the bridge name for the PIF's VLAN slave.
499 (xapi will create a datapath named with the bridge name even though we won't
503 pifrec = db.get_pif_record(pif)
505 if pifrec['VLAN'] == '-1':
506 return bridge_name(pif)
508 return bridge_name(get_vlan_slave_of_pif(pif))
511 """Return the the name of the network device that carries the
512 IP configuration (if any) associated with pif.
513 The ipdev name is the same as the bridge name.
516 pifrec = db.get_pif_record(pif)
517 return bridge_name(pif)
519 def physdev_names(pif):
520 """Return the name(s) of the physical network device(s) associated with pif.
521 For a VLAN PIF, the physical devices are the VLAN slave's physical devices.
522 For a bond master PIF, the physical devices are the bond slaves.
523 For a non-VLAN, non-bond master PIF, the physical device is the PIF itself.
526 pifrec = db.get_pif_record(pif)
528 if pifrec['VLAN'] != '-1':
529 return physdev_names(get_vlan_slave_of_pif(pif))
530 elif len(pifrec['bond_master_of']) != 0:
532 for slave in get_bond_slaves_of_pif(pif):
533 physdevs += physdev_names(slave)
536 return [pifrec['device']]
538 def log_pif_action(action, pif):
539 pifrec = db.get_pif_record(pif)
540 pifrec['action'] = action
541 pifrec['interface-name'] = interface_name(pif)
542 if action == "rewrite":
543 pifrec['message'] = "Rewrite PIF %(uuid)s configuration" % pifrec
545 pifrec['message'] = "Bring %(action)s PIF %(uuid)s" % pifrec
546 log("%(message)s: %(interface-name)s configured as %(ip_configuration_mode)s" % pifrec)
548 def get_bond_masters_of_pif(pif):
549 """Returns a list of PIFs which are bond masters of this PIF"""
551 pifrec = db.get_pif_record(pif)
553 bso = pifrec['bond_slave_of']
555 # bond-slave-of is currently a single reference but in principle a
556 # PIF could be a member of several bonds which are not
557 # concurrently attached. Be robust to this possibility.
558 if not bso or bso == "OpaqueRef:NULL":
560 elif not type(bso) == list:
563 bondrecs = [db.get_bond_record(bond) for bond in bso]
564 bondrecs = [rec for rec in bondrecs if rec]
566 return [bond['master'] for bond in bondrecs]
568 def get_bond_slaves_of_pif(pif):
569 """Returns a list of PIFs which make up the given bonded pif."""
571 pifrec = db.get_pif_record(pif)
573 bmo = pifrec['bond_master_of']
575 raise Error("Bond-master-of contains too many elements")
580 bondrec = db.get_bond_record(bmo[0])
582 raise Error("No bond record for bond master PIF")
584 return bondrec['slaves']
586 def get_vlan_slave_of_pif(pif):
587 """Find the PIF which is the VLAN slave of pif.
589 Returns the 'physical' PIF underneath the a VLAN PIF @pif."""
591 pifrec = db.get_pif_record(pif)
593 vlan = pifrec['VLAN_master_of']
594 if not vlan or vlan == "OpaqueRef:NULL":
595 raise Error("PIF is not a VLAN master")
597 vlanrec = db.get_vlan_record(vlan)
599 raise Error("No VLAN record found for PIF")
601 return vlanrec['tagged_PIF']
603 def get_vlan_masters_of_pif(pif):
604 """Returns a list of PIFs which are VLANs on top of the given pif."""
606 pifrec = db.get_pif_record(pif)
607 vlans = [db.get_vlan_record(v) for v in pifrec['VLAN_slave_of']]
608 return [v['untagged_PIF'] for v in vlans if v and db.pif_exists(v['untagged_PIF'])]
610 def interface_deconfigure_commands(interface):
611 # The use of [!0-9] keeps an interface of 'eth0' from matching
612 # VLANs attached to eth0 (such as 'eth0.123'), which are distinct
614 return ['--del-match=bridge.*.port=%s' % interface,
615 '--del-match=bonding.%s.[!0-9]*' % interface,
616 '--del-match=bonding.*.slave=%s' % interface,
617 '--del-match=vlan.%s.[!0-9]*' % interface,
618 '--del-match=port.%s.[!0-9]*' % interface,
619 '--del-match=iface.%s.[!0-9]*' % interface]
621 def run_command(command):
622 log("Running command: " + ' '.join(command))
623 if os.spawnl(os.P_WAIT, command[0], *command) != 0:
624 log("Command failed: " + ' '.join(command))
628 def down_netdev(interface, deconfigure=True):
629 if not interface_exists(interface):
630 log("down_netdev: interface %s does not exist, ignoring" % interface)
632 argv = ["/sbin/ifconfig", interface, 'down']
637 pidfile_name = '/var/run/dhclient-%s.pid' % interface
640 pidfile = open(pidfile_name, 'r')
641 os.kill(int(pidfile.readline()), signal.SIGTERM)
647 # Remove dhclient pidfile.
649 os.remove(pidfile_name)
654 def up_netdev(interface):
655 run_command(["/sbin/ifconfig", interface, 'up'])
657 def find_distinguished_pifs(pif):
658 """Returns the PIFs on host that own DNS and the default route.
659 The peerdns pif will be the one with pif::other-config:peerdns=true, or the mgmt pif if none have this set.
660 The gateway pif will be the one with pif::other-config:defaultroute=true, or the mgmt pif if none have this set.
662 Note: we prune out the bond master pif (if it exists).
663 This is because when we are called to bring up an interface with a bond master, it is implicit that
664 we should bring down that master."""
666 pifrec = db.get_pif_record(pif)
668 pifs = [ __pif for __pif in db.get_all_pifs() if
669 (not __pif in get_bond_masters_of_pif(pif)) ]
672 defaultroute_pif = None
674 # loop through all the pifs on this host looking for one with
675 # other-config:peerdns = true, and one with
676 # other-config:default-route=true
678 __pifrec = db.get_pif_record(__pif)
679 __oc = __pifrec['other_config']
680 if __oc.has_key('peerdns') and __oc['peerdns'] == 'true':
681 if peerdns_pif == None:
684 log('Warning: multiple pifs with "peerdns=true" - choosing %s and ignoring %s' % \
685 (db.get_pif_record(peerdns_pif)['device'], __pifrec['device']))
686 if __oc.has_key('defaultroute') and __oc['defaultroute'] == 'true':
687 if defaultroute_pif == None:
688 defaultroute_pif = __pif
690 log('Warning: multiple pifs with "defaultroute=true" - choosing %s and ignoring %s' % \
691 (db.get_pif_record(defaultroute_pif)['device'], __pifrec['device']))
693 # If no pif is explicitly specified then use the mgmt pif for peerdns/defaultroute
694 if peerdns_pif == None:
695 peerdns_pif = management_pif
696 if defaultroute_pif == None:
697 defaultroute_pif = management_pif
699 return peerdns_pif, defaultroute_pif
701 def ethtool_settings(oc):
702 # Options for "ethtool -s"
704 if oc.has_key('ethtool-speed'):
705 val = oc['ethtool-speed']
706 if val in ["10", "100", "1000"]:
707 settings += ['speed', val]
709 log("Invalid value for ethtool-speed = %s. Must be 10|100|1000." % val)
710 if oc.has_key('ethtool-duplex'):
711 val = oc['ethtool-duplex']
712 if val in ["10", "100", "1000"]:
713 settings += ['duplex', 'val']
715 log("Invalid value for ethtool-duplex = %s. Must be half|full." % val)
716 if oc.has_key('ethtool-autoneg'):
717 val = oc['ethtool-autoneg']
718 if val in ["true", "on"]:
719 settings += ['autoneg', 'on']
720 elif val in ["false", "off"]:
721 settings += ['autoneg', 'off']
723 log("Invalid value for ethtool-autoneg = %s. Must be on|true|off|false." % val)
725 # Options for "ethtool -K"
727 for opt in ("rx", "tx", "sg", "tso", "ufo", "gso"):
728 if oc.has_key("ethtool-" + opt):
729 val = oc["ethtool-" + opt]
730 if val in ["true", "on"]:
731 offload += [opt, 'on']
732 elif val in ["false", "off"]:
733 offload += [opt, 'off']
735 log("Invalid value for ethtool-%s = %s. Must be on|true|off|false." % (opt, val))
737 return settings, offload
739 def configure_netdev(pif):
740 pifrec = db.get_pif_record(pif)
741 datapath = datapath_name(pif)
742 ipdev = ipdev_name(pif)
744 nw = pifrec['network']
745 nwrec = db.get_network_record(nw)
747 ifconfig_argv = ['/sbin/ifconfig', ipdev, 'up']
749 if pifrec['ip_configuration_mode'] == "DHCP":
751 elif pifrec['ip_configuration_mode'] == "Static":
752 ifconfig_argv += [pifrec['IP']]
753 ifconfig_argv += ['netmask', pifrec['netmask']]
754 gateway = pifrec['gateway']
755 elif pifrec['ip_configuration_mode'] == "None":
759 raise Error("Unknown IP-configuration-mode %s" % pifrec['ip_configuration_mode'])
762 if pifrec.has_key('other_config'):
763 oc = pifrec['other_config']
764 if oc.has_key('mtu'):
765 int(oc['mtu']) # Check that the value is an integer
766 ifconfig_argv += ['mtu', oc['mtu']]
768 run_command(ifconfig_argv)
770 (peerdns_pif, defaultroute_pif) = find_distinguished_pifs(pif)
772 if peerdns_pif == pif:
773 f = ConfigurationFile('resolv.conf', "/etc")
774 if oc.has_key('domain'):
775 f.write("search %s\n" % oc['domain'])
776 for dns in pifrec['DNS'].split(","):
777 f.write("nameserver %s\n" % dns)
782 if defaultroute_pif == pif and gateway != '':
783 run_command(['/sbin/ip', 'route', 'replace', 'default',
784 'via', gateway, 'dev', ipdev])
786 if oc.has_key('static-routes'):
787 for line in oc['static-routes'].split(','):
788 network, masklen, gateway = line.split('/')
789 run_command(['/sbin/ip', 'route', 'add',
790 '%s/%s' % (netmask, masklen), 'via', gateway,
793 settings, offload = ethtool_settings(oc)
795 run_command(['/sbin/ethtool', '-s', ipdev] + settings)
797 run_command(['/sbin/ethtool', '-K', ipdev] + offload)
799 if pifrec['ip_configuration_mode'] == "DHCP":
801 print "Determining IP information for %s..." % ipdev,
802 argv = ['/sbin/dhclient', '-q',
803 '-lf', '/var/lib/dhclient/dhclient-%s.leases' % ipdev,
804 '-pf', '/var/run/dhclient-%s.pid' % ipdev,
806 if run_command(argv):
811 def modify_config(commands):
812 run_command(['/root/vswitch/bin/ovs-cfg-mod', '-vANY:console:emer',
813 '-F', '/etc/ovs-vswitchd.conf']
815 run_command(['/sbin/service', 'vswitch', 'reload'])
817 def is_bond_pif(pif):
818 pifrec = db.get_pif_record(pif)
819 return len(pifrec['bond_master_of']) != 0
821 def configure_bond(pif):
822 pifrec = db.get_pif_record(pif)
823 interface = interface_name(pif)
824 ipdev = ipdev_name(pif)
825 datapath = datapath_name(pif)
826 physdevs = physdev_names(pif)
828 argv = ['--del-match=bonding.%s.[!0-9]*' % interface]
829 argv += ["--add=bonding.%s.slave=%s" % (interface, slave)
830 for slave in physdevs]
834 "mode": "balance-slb",
840 # override defaults with values from other-config whose keys
842 oc = pifrec['other_config']
843 overrides = filter(lambda (key,val):
844 key.startswith("bond-"), oc.items())
845 overrides = map(lambda (key,val): (key[5:], val), overrides)
846 bond_options.update(overrides)
847 for (name,val) in bond_options.items():
848 argv += ["--add=bonding.%s.%s=%s" % (interface, name, val)]
852 pifrec = db.get_pif_record(pif)
854 bridge = bridge_name(pif)
855 interface = interface_name(pif)
856 ipdev = ipdev_name(pif)
857 datapath = datapath_name(pif)
858 physdevs = physdev_names(pif)
860 if pifrec['VLAN'] != '-1':
861 vlan_slave = get_vlan_slave_of_pif(pif)
862 if vlan_slave and is_bond_pif(vlan_slave):
863 bond_master = vlan_slave
864 elif is_bond_pif(pif):
869 bond_slaves = get_bond_slaves_of_pif(bond_master)
872 bond_masters = get_bond_masters_of_pif(pif)
874 # Support "rpm -e vswitch" gracefully by keeping Centos configuration
875 # files up-to-date, even though we don't use them or need them.
876 f = configure_pif(pif)
877 mode = pifrec['ip_configuration_mode']
879 log("Configuring %s using %s configuration" % (bridge, mode))
880 br = open_network_ifcfg(pif)
881 configure_network(pif, br)
885 log("Configuring %s using %s configuration" % (interface, mode))
886 configure_network(pif, f)
888 for master in bond_masters:
889 master_bridge = bridge_name(master)
890 removed = unconfigure_pif(master)
891 f.attach_child(removed)
893 removed = open_network_ifcfg(master)
894 log("Unlinking stale file %s" % removed.path())
896 f.attach_child(removed)
898 # /etc/xensource/scripts/vif needs to know where to add VIFs.
900 if not os.path.exists(vswitch_config_dir):
901 os.mkdir(vswitch_config_dir)
902 br = ConfigurationFile("br-%s" % bridge, vswitch_config_dir)
903 br.write("VLAN_SLAVE=%s\n" % datapath)
904 br.write("VLAN_VID=%s\n" % pifrec['VLAN'])
908 # Update all configuration files (both ours and Centos's).
912 # "ifconfig down" the network device and delete its IP address, etc.
914 for physdev in physdevs:
917 # If we are bringing up a bond, remove IP addresses from the
918 # slaves (because we are implicitly being asked to take them down).
920 # Conversely, if we are bringing up an interface that has bond
921 # masters, remove IP addresses from the bond master (because we
922 # are implicitly being asked to take it down).
923 for bond_pif in bond_slaves + bond_masters:
924 run_command(["/sbin/ifconfig", ipdev_name(bond_pif), '0.0.0.0'])
926 # Remove all keys related to pif and any bond masters linked to PIF.
927 del_ports = [ipdev] + physdevs + bond_masters
928 if vlan_slave and bond_master:
929 del_ports += [interface_name(bond_master)]
931 # What ports do we need to add to the datapath?
933 # We definitely need the ipdev, and ordinarily we want the
934 # physical devices too, but for bonds we need the bond as bridge
936 add_ports = [ipdev, datapath]
938 add_ports += physdevs
940 add_ports += [interface_name(bond_master)]
942 # What ports do we need to delete?
944 # - All the ports that we add, to avoid duplication and to drop
945 # them from another datapath in case they're misassigned.
947 # - The physical devices, since they will either be in add_ports
948 # or added to the bonding device (see below).
950 # - The bond masters for pif. (Ordinarily pif shouldn't have any
951 # bond masters. If it does then interface-reconfigure is
952 # implicitly being asked to take them down.)
953 del_ports = add_ports + physdevs + bond_masters
955 # What networks does this datapath carry?
957 # - The network corresponding to the datapath's PIF.
959 # - The networks corresponding to any VLANs attached to the
962 for nwpif in db.get_pifs_by_device({'device': pifrec['device']}):
963 net = db.get_pif_record(nwpif)['network']
964 network_uuids += [db.get_network_record(net)['uuid']]
966 # Bring up bond slaves early, because ovs-vswitchd initially
967 # enables or disables bond slaves based on whether carrier is
968 # detected when they are added, and a network device that is down
969 # always reports "no carrier".
970 bond_slave_physdevs = []
971 for slave in bond_slaves:
972 bond_slave_physdevs += physdev_names(slave)
973 for slave_physdev in bond_slave_physdevs:
974 up_netdev(slave_physdev)
976 # Now modify the ovs-vswitchd config file.
978 for port in set(del_ports):
979 argv += interface_deconfigure_commands(port)
980 for port in set(add_ports):
981 argv += ['--add=bridge.%s.port=%s' % (datapath, port)]
983 argv += ['--add=vlan.%s.tag=%s' % (ipdev, pifrec['VLAN'])]
984 argv += ['--add=iface.%s.internal=true' % (ipdev)]
986 # xapi creates a bridge by the name of the ipdev and requires
987 # that the IP address will be on it. We need to delete this
988 # bridge because we need that device to be a member of our
990 argv += ['--del-match=bridge.%s.[!0-9]*' % ipdev]
992 # xapi insists that its attempts to create the bridge succeed,
993 # so force that to happen.
994 argv += ['--add=iface.%s.fake-bridge=true' % (ipdev)]
997 os.unlink("%s/br-%s" % (vswitch_config_dir, bridge))
1000 argv += ['--del-match=bridge.%s.xs-network-uuids=*' % datapath]
1001 argv += ['--add=bridge.%s.xs-network-uuids=%s' % (datapath, uuid)
1002 for uuid in set(network_uuids)]
1004 argv += configure_bond(bond_master)
1007 # Configure network devices.
1008 configure_netdev(pif)
1010 # Bring up VLAN slave, plus physical devices other than bond
1011 # slaves (which we brought up earlier).
1013 up_netdev(ipdev_name(vlan_slave))
1014 for physdev in set(physdevs) - set(bond_slave_physdevs):
1017 # Update /etc/issue (which contains the IP address of the management interface)
1018 os.system("/sbin/update-issue")
1021 # There seems to be a race somewhere: without this sleep, using
1022 # XenCenter to create a bond that becomes the management interface
1023 # fails with "The underlying connection was closed: A connection that
1024 # was expected to be kept alive was closed by the server." on every
1025 # second or third try, even though /var/log/messages doesn't show
1028 # The race is probably present even without vswitch, but bringing up a
1029 # bond without vswitch involves a built-in pause of 10 seconds or more
1030 # to wait for the bond to transition from learning to forwarding state.
1033 def action_down(pif):
1034 rec = db.get_pif_record(pif)
1035 interface = interface_name(pif)
1036 bridge = bridge_name(pif)
1037 ipdev = ipdev_name(pif)
1039 # Support "rpm -e vswitch" gracefully by keeping Centos configuration
1040 # files up-to-date, even though we don't use them or need them.
1041 f = unconfigure_pif(pif)
1043 br = open_network_ifcfg(pif)
1044 log("Unlinking stale file %s" % br.path())
1051 log("action_down failed to apply changes: %s" % e.msg)
1056 if rec['VLAN'] != '-1':
1057 # Get rid of the VLAN device itself.
1059 argv += interface_deconfigure_commands(ipdev)
1061 # If the VLAN's slave is attached, stop here.
1062 slave = get_vlan_slave_of_pif(pif)
1063 if db.get_pif_record(slave)['currently_attached']:
1064 log("VLAN slave is currently attached")
1068 # If the VLAN's slave has other VLANs that are attached, stop here.
1069 masters = get_vlan_masters_of_pif(slave)
1071 if m != pif and db.get_pif_record(m)['currently_attached']:
1072 log("VLAN slave has other master %s" % interface_naem(m))
1076 # Otherwise, take down the VLAN's slave too.
1077 log("No more masters, bring down vlan slave %s" % interface_name(slave))
1080 # Stop here if this PIF has attached VLAN masters.
1081 vlan_masters = get_vlan_masters_of_pif(pif)
1082 log("VLAN masters of %s - %s" % (rec['device'], [interface_name(m) for m in vlan_masters]))
1083 for m in vlan_masters:
1084 if db.get_pif_record(m)['currently_attached']:
1085 log("Leaving %s up due to currently attached VLAN master %s" % (interface, interface_name(m)))
1088 # pif is now either a bond or a physical device which needs to be
1089 # brought down. pif might have changed so re-check all its attributes.
1090 rec = db.get_pif_record(pif)
1091 interface = interface_name(pif)
1092 bridge = bridge_name(pif)
1093 ipdev = ipdev_name(pif)
1096 bond_slaves = get_bond_slaves_of_pif(pif)
1097 log("bond slaves of %s - %s" % (rec['device'], [interface_name(s) for s in bond_slaves]))
1098 for slave in bond_slaves:
1099 slave_interface = interface_name(slave)
1100 log("bring down bond slave %s" % slave_interface)
1101 argv += interface_deconfigure_commands(slave_interface)
1102 down_netdev(slave_interface)
1104 argv += interface_deconfigure_commands(ipdev)
1107 argv += ['--del-match', 'bridge.%s.*' % datapath_name(pif)]
1108 argv += ['--del-match', 'bonding.%s.[!0-9]*' % interface]
1111 def action_rewrite(pif):
1112 # Support "rpm -e vswitch" gracefully by keeping Centos configuration
1113 # files up-to-date, even though we don't use them or need them.
1114 pifrec = db.get_pif_record(pif)
1115 f = configure_pif(pif)
1116 interface = interface_name(pif)
1117 bridge = bridge_name(pif)
1118 mode = pifrec['ip_configuration_mode']
1120 log("Configuring %s using %s configuration" % (bridge, mode))
1121 br = open_network_ifcfg(pif)
1122 configure_network(pif, br)
1126 log("Configuring %s using %s configuration" % (interface, mode))
1127 configure_network(pif, f)
1133 log("failed to apply changes: %s" % e.msg)
1137 # We have no code of our own to run here.
1140 def main(argv=None):
1141 global output_directory, management_pif
1147 force_interface = None
1148 force_management = False
1156 longops = [ "output-directory=",
1157 "pif=", "pif-uuid=",
1163 "device=", "mode=", "ip=", "netmask=", "gateway=",
1165 arglist, args = getopt.gnu_getopt(argv[1:], shortops, longops)
1166 except getopt.GetoptError, msg:
1169 force_rewrite_config = {}
1172 if o == "--output-directory":
1173 output_directory = a
1176 elif o == "--pif-uuid":
1178 elif o == "--session":
1180 elif o == "--force-interface" or o == "--force":
1182 elif o == "--management":
1183 force_management = True
1184 elif o in ["--device", "--mode", "--ip", "--netmask", "--gateway"]:
1185 force_rewrite_config[o[2:]] = a
1186 elif o == "-h" or o == "--help":
1187 print __doc__ % {'command-name': os.path.basename(argv[0])}
1190 if not debug_mode():
1191 syslog.openlog(os.path.basename(argv[0]))
1192 log("Called as " + str.join(" ", argv))
1194 raise Usage("Required option <action> not present")
1196 raise Usage("Too many arguments")
1199 # backwards compatibility
1200 if action == "rewrite-configuration": action = "rewrite"
1202 if output_directory and ( session or pif ):
1203 raise Usage("--session/--pif cannot be used with --output-directory")
1204 if ( session or pif ) and pif_uuid:
1205 raise Usage("--session/--pif and --pif-uuid are mutually exclusive.")
1206 if ( session and not pif ) or ( not session and pif ):
1207 raise Usage("--session and --pif must be used together.")
1208 if force_interface and ( session or pif or pif_uuid ):
1209 raise Usage("--force is mutually exclusive with --session, --pif and --pif-uuid")
1210 if len(force_rewrite_config) and not (force_interface and action == "rewrite"):
1211 raise Usage("\"--force rewrite\" needed for --device, --mode, --ip, --netmask, and --gateway")
1215 log("Force interface %s %s" % (force_interface, action))
1217 if action == "rewrite":
1218 action_force_rewrite(force_interface, force_rewrite_config)
1220 db = DatabaseCache(cache_file=dbcache_file)
1221 pif = db.get_pif_by_bridge(force_interface)
1222 management_pif = db.get_management_pif()
1226 elif action == "down":
1229 raise Usage("Unknown action %s" % action)
1231 db = DatabaseCache(session_ref=session)
1234 pif = db.get_pif_by_uuid(pif_uuid)
1237 raise Usage("No PIF given")
1239 if force_management:
1240 # pif is going to be the management pif
1241 management_pif = pif
1243 # pif is not going to be the management pif.
1244 # Search DB cache for pif on same host with management=true
1245 pifrec = db.get_pif_record(pif)
1246 management_pif = db.get_management_pif()
1248 log_pif_action(action, pif)
1250 if not check_allowed(pif):
1255 elif action == "down":
1257 elif action == "rewrite":
1260 raise Usage("Unknown action %s" % action)
1263 pifrec = db.get_pif_record(pif)
1264 db.save(dbcache_file)
1267 print >>sys.stderr, err.msg
1268 print >>sys.stderr, "For help use --help."
1276 # The following code allows interface-reconfigure to keep Centos
1277 # network configuration files up-to-date, even though the vswitch
1278 # never uses them. In turn, that means that "rpm -e vswitch" does not
1279 # have to update any configuration files.
1281 def configure_ethtool(oc, f):
1282 # Options for "ethtool -s"
1284 setting_opts = ["autoneg", "speed", "duplex"]
1285 # Options for "ethtool -K"
1287 offload_opts = ["rx", "tx", "sg", "tso", "ufo", "gso"]
1289 for opt in [opt for opt in setting_opts + offload_opts if oc.has_key("ethtool-" + opt)]:
1290 val = oc["ethtool-" + opt]
1292 if opt in ["speed"]:
1293 if val in ["10", "100", "1000"]:
1294 val = "speed " + val
1296 log("Invalid value for ethtool-speed = %s. Must be 10|100|1000." % val)
1298 elif opt in ["duplex"]:
1299 if val in ["half", "full"]:
1300 val = "duplex " + val
1302 log("Invalid value for ethtool-duplex = %s. Must be half|full." % val)
1304 elif opt in ["autoneg"] + offload_opts:
1305 if val in ["true", "on"]:
1307 elif val in ["false", "off"]:
1310 log("Invalid value for ethtool-%s = %s. Must be on|true|off|false." % (opt, val))
1313 if opt in setting_opts:
1314 if val and settings:
1315 settings = settings + " " + val
1318 elif opt in offload_opts:
1320 offload = offload + " " + val
1325 f.write("ETHTOOL_OPTS=\"%s\"\n" % settings)
1327 f.write("ETHTOOL_OFFLOAD_OPTS=\"%s\"\n" % offload)
1329 def configure_mtu(oc, f):
1330 if not oc.has_key('mtu'):
1334 mtu = int(oc['mtu'])
1335 f.write("MTU=%d\n" % mtu)
1336 except ValueError, x:
1337 log("Invalid value for mtu = %s" % mtu)
1339 def configure_static_routes(interface, oc, f):
1340 """Open a route-<interface> file for static routes.
1342 Opens the static routes configuration file for interface and writes one
1343 line for each route specified in the network's other config "static-routes" value.
1345 interface ( RO): xenbr1
1346 other-config (MRW): static-routes: 172.16.0.0/15/192.168.0.3,172.18.0.0/16/192.168.0.4;...
1348 Then route-xenbr1 should be
1349 172.16.0.0/15 via 192.168.0.3 dev xenbr1
1350 172.18.0.0/16 via 192.168.0.4 dev xenbr1
1352 fname = "route-%s" % interface
1353 if oc.has_key('static-routes'):
1354 # The key is present - extract comma seperates entries
1355 lines = oc['static-routes'].split(',')
1357 # The key is not present, i.e. there are no static routes
1360 child = ConfigurationFile(fname)
1361 child.write("# DO NOT EDIT: This file (%s) was autogenerated by %s\n" % \
1362 (os.path.basename(child.path()), os.path.basename(sys.argv[0])))
1366 network, masklen, gateway = l.split('/')
1367 child.write("%s/%s via %s dev %s\n" % (network, masklen, gateway, interface))
1369 f.attach_child(child)
1372 except ValueError, e:
1373 log("Error in other-config['static-routes'] format for network %s: %s" % (interface, e))
1375 def __open_ifcfg(interface):
1376 """Open a network interface configuration file.
1378 Opens the configuration file for interface, writes a header and
1379 common options and returns the file object.
1381 fname = "ifcfg-%s" % interface
1382 f = ConfigurationFile(fname)
1384 f.write("# DO NOT EDIT: This file (%s) was autogenerated by %s\n" % \
1385 (os.path.basename(f.path()), os.path.basename(sys.argv[0])))
1386 f.write("XEMANAGED=yes\n")
1387 f.write("DEVICE=%s\n" % interface)
1388 f.write("ONBOOT=no\n")
1392 def open_network_ifcfg(pif):
1393 bridge = bridge_name(pif)
1394 interface = interface_name(pif)
1396 return __open_ifcfg(bridge)
1398 return __open_ifcfg(interface)
1401 def open_pif_ifcfg(pif):
1402 pifrec = db.get_pif_record(pif)
1404 log("Configuring %s (%s)" % (interface_name(pif), pifrec['MAC']))
1406 f = __open_ifcfg(interface_name(pif))
1408 if pifrec.has_key('other_config'):
1409 configure_ethtool(pifrec['other_config'], f)
1410 configure_mtu(pifrec['other_config'], f)
1414 def configure_network(pif, f):
1415 """Write the configuration file for a network.
1417 Writes configuration derived from the network object into the relevant
1418 ifcfg file. The configuration file is passed in, but if the network is
1419 bridgeless it will be ifcfg-<interface>, otherwise it will be ifcfg-<bridge>.
1421 This routine may also write ifcfg files of the networks corresponding to other PIFs
1422 in order to maintain consistency.
1425 pif: Opaque_ref of pif
1426 f : ConfigurationFile(/path/to/ifcfg) to which we append network configuration
1429 pifrec = db.get_pif_record(pif)
1430 nw = pifrec['network']
1431 nwrec = db.get_network_record(nw)
1433 bridge = bridge_name(pif)
1434 interface = interface_name(pif)
1440 if nwrec.has_key('other_config'):
1441 configure_ethtool(nwrec['other_config'], f)
1442 configure_mtu(nwrec['other_config'], f)
1443 configure_static_routes(device, nwrec['other_config'], f)
1446 if pifrec.has_key('other_config'):
1447 oc = pifrec['other_config']
1449 if device == bridge:
1450 f.write("TYPE=Bridge\n")
1451 f.write("DELAY=0\n")
1452 f.write("STP=off\n")
1453 f.write("PIFDEV=%s\n" % interface_name(pif))
1455 if pifrec['ip_configuration_mode'] == "DHCP":
1456 f.write("BOOTPROTO=dhcp\n")
1457 f.write("PERSISTENT_DHCLIENT=yes\n")
1458 elif pifrec['ip_configuration_mode'] == "Static":
1459 f.write("BOOTPROTO=none\n")
1460 f.write("NETMASK=%(netmask)s\n" % pifrec)
1461 f.write("IPADDR=%(IP)s\n" % pifrec)
1462 f.write("GATEWAY=%(gateway)s\n" % pifrec)
1463 elif pifrec['ip_configuration_mode'] == "None":
1464 f.write("BOOTPROTO=none\n")
1466 raise Error("Unknown ip-configuration-mode %s" % pifrec['ip_configuration_mode'])
1468 if pifrec.has_key('DNS') and pifrec['DNS'] != "":
1469 ServerList = pifrec['DNS'].split(",")
1470 for i in range(len(ServerList)): f.write("DNS%d=%s\n" % (i+1, ServerList[i]))
1471 if oc and oc.has_key('domain'):
1472 f.write("DOMAIN='%s'\n" % oc['domain'].replace(',', ' '))
1474 # We only allow one ifcfg-xenbr* to have PEERDNS=yes and there can be only one GATEWAYDEV in /etc/sysconfig/network.
1475 # The peerdns pif will be the one with pif::other-config:peerdns=true, or the mgmt pif if none have this set.
1476 # The gateway pif will be the one with pif::other-config:defaultroute=true, or the mgmt pif if none have this set.
1478 # Work out which pif on this host should be the one with PEERDNS=yes and which should be the GATEWAYDEV
1480 # Note: we prune out the bond master pif (if it exists).
1481 # This is because when we are called to bring up an interface with a bond master, it is implicit that
1482 # we should bring down that master.
1483 pifs_on_host = [ __pif for __pif in db.get_all_pifs() if
1484 not __pif in get_bond_masters_of_pif(pif) ]
1485 other_pifs_on_host = [ __pif for __pif in pifs_on_host if __pif != pif ]
1488 defaultroute_pif = None
1490 # loop through all the pifs on this host looking for one with
1491 # other-config:peerdns = true, and one with
1492 # other-config:default-route=true
1493 for __pif in pifs_on_host:
1494 __pifrec = db.get_pif_record(__pif)
1495 __oc = __pifrec['other_config']
1496 if __oc.has_key('peerdns') and __oc['peerdns'] == 'true':
1497 if peerdns_pif == None:
1500 log('Warning: multiple pifs with "peerdns=true" - choosing %s and ignoring %s' % \
1501 (db.get_pif_record(peerdns_pif)['device'], __pifrec['device']))
1502 if __oc.has_key('defaultroute') and __oc['defaultroute'] == 'true':
1503 if defaultroute_pif == None:
1504 defaultroute_pif = __pif
1506 log('Warning: multiple pifs with "defaultroute=true" - choosing %s and ignoring %s' % \
1507 (db.get_pif_record(defaultroute_pif)['device'], __pifrec['device']))
1509 # If no pif is explicitly specified then use the mgmt pif for peerdns/defaultroute
1510 if peerdns_pif == None:
1511 peerdns_pif = management_pif
1512 if defaultroute_pif == None:
1513 defaultroute_pif = management_pif
1515 # Update all the other network's ifcfg files and ensure consistency
1516 for __pif in other_pifs_on_host:
1517 __f = open_network_ifcfg(__pif)
1518 peerdns_line_wanted = 'PEERDNS=%s\n' % ((__pif == peerdns_pif) and 'yes' or 'no')
1519 lines = __f.readlines()
1521 if not peerdns_line_wanted in lines:
1522 # the PIF selected for DNS has changed and as a result this ifcfg file needs rewriting
1524 if not line.lstrip().startswith('PEERDNS'):
1526 log("Setting %s in %s" % (peerdns_line_wanted.strip(), __f.path()))
1527 __f.write(peerdns_line_wanted)
1532 # There is no need to change this ifcfg file. So don't attach_child.
1535 # ... and for this pif too
1536 f.write('PEERDNS=%s\n' % ((pif == peerdns_pif) and 'yes' or 'no'))
1539 fnetwork = ConfigurationFile("network", "/etc/sysconfig")
1540 for line in fnetwork.readlines():
1541 if line.lstrip().startswith('GATEWAY') :
1543 fnetwork.write(line)
1544 if defaultroute_pif:
1545 gatewaydev = bridge_name(defaultroute_pif)
1547 gatewaydev = interface_name(defaultroute_pif)
1548 fnetwork.write('GATEWAYDEV=%s\n' % gatewaydev)
1550 f.attach_child(fnetwork)
1555 def configure_physical_interface(pif):
1556 """Write the configuration for a physical interface.
1558 Writes the configuration file for the physical interface described by
1561 Returns the open file handle for the interface configuration file.
1564 pifrec = db.get_pif_record(pif)
1566 f = open_pif_ifcfg(pif)
1568 f.write("TYPE=Ethernet\n")
1569 f.write("HWADDR=%(MAC)s\n" % pifrec)
1573 def configure_bond_interface(pif):
1574 """Write the configuration for a bond interface.
1576 Writes the configuration file for the bond interface described by
1577 the pif object. Handles writing the configuration for the slave
1580 Returns the open file handle for the bond interface configuration
1584 pifrec = db.get_pif_record(pif)
1585 oc = pifrec['other_config']
1586 f = open_pif_ifcfg(pif)
1588 if pifrec['MAC'] != "":
1589 f.write("MACADDR=%s\n" % pifrec['MAC'])
1591 for slave in get_bond_slaves_of_pif(pif):
1592 s = configure_physical_interface(slave)
1593 s.write("MASTER=%(device)s\n" % pifrec)
1594 s.write("SLAVE=yes\n")
1598 # The bond option defaults
1600 "mode": "balance-slb",
1607 # override defaults with values from other-config whose keys being with "bond-"
1608 overrides = filter(lambda (key,val): key.startswith("bond-"), oc.items())
1609 overrides = map(lambda (key,val): (key[5:], val), overrides)
1610 bond_options.update(overrides)
1612 # write the bond options to ifcfg-bondX
1613 f.write('BONDING_OPTS="')
1614 for (name,val) in bond_options.items():
1615 f.write("%s=%s " % (name,val))
1619 def configure_vlan_interface(pif):
1620 """Write the configuration for a VLAN interface.
1622 Writes the configuration file for the VLAN interface described by
1623 the pif object. Handles writing the configuration for the master
1624 interface if necessary.
1626 Returns the open file handle for the VLAN interface configuration
1630 slave = configure_pif(get_vlan_slave_of_pif(pif))
1633 f = open_pif_ifcfg(pif)
1634 f.write("VLAN=yes\n")
1635 f.attach_child(slave)
1639 def configure_pif(pif):
1640 """Write the configuration for a PIF object.
1642 Writes the configuration file the PIF and all dependent
1643 interfaces (bond slaves and VLAN masters etc).
1645 Returns the open file handle for the interface configuration file.
1648 pifrec = db.get_pif_record(pif)
1650 if pifrec['VLAN'] != '-1':
1651 f = configure_vlan_interface(pif)
1652 elif len(pifrec['bond_master_of']) != 0:
1653 f = configure_bond_interface(pif)
1655 f = configure_physical_interface(pif)
1657 bridge = bridge_name(pif)
1659 f.write("BRIDGE=%s\n" % bridge)
1663 def unconfigure_pif(pif):
1664 """Clear up the files created by configure_pif"""
1665 f = open_pif_ifcfg(pif)
1666 log("Unlinking stale file %s" % f.path())
1670 if __name__ == "__main__":
1676 err = traceback.format_exception(*ex)
1680 if not debug_mode():