3 # Copyright (c) 2008,2009 Citrix Systems, Inc. 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
68 output_directory = None
73 dbcache_file = "/etc/ovs-vswitch.dbcache"
74 vswitch_config_dir = "/etc/openvswitch"
76 class Usage(Exception):
77 def __init__(self, msg):
78 Exception.__init__(self)
81 class Error(Exception):
82 def __init__(self, msg):
83 Exception.__init__(self)
86 class ConfigurationFile(object):
87 """Write a file, tracking old and new versions.
89 Supports writing a new version of a file and applying and
90 reverting those changes.
93 __STATE = {"OPEN":"OPEN",
94 "NOT-APPLIED":"NOT-APPLIED", "APPLIED":"APPLIED",
95 "REVERTED":"REVERTED", "COMMITTED": "COMMITTED"}
97 def __init__(self, fname, path="/etc/sysconfig/network-scripts"):
99 self.__state = self.__STATE['OPEN']
104 dirname = output_directory
108 self.__path = os.path.join(dirname, fname)
109 self.__oldpath = os.path.join(dirname, "." + fname + ".xapi-old")
110 self.__newpath = os.path.join(dirname, "." + fname + ".xapi-new")
111 self.__unlink = False
113 self.__f = open(self.__newpath, "w")
115 def attach_child(self, child):
116 self.__children.append(child)
123 return open(self.path()).readlines()
127 def write(self, args):
128 if self.__state != self.__STATE['OPEN']:
129 raise Error("Attempt to write to file in state %s" % self.__state)
133 if self.__state != self.__STATE['OPEN']:
134 raise Error("Attempt to unlink file in state %s" % self.__state)
137 self.__state = self.__STATE['NOT-APPLIED']
140 if self.__state != self.__STATE['OPEN']:
141 raise Error("Attempt to close file in state %s" % self.__state)
144 self.__state = self.__STATE['NOT-APPLIED']
147 if self.__state != self.__STATE['NOT-APPLIED']:
148 raise Error("Attempt to compare file in state %s" % self.__state)
153 if self.__state != self.__STATE['NOT-APPLIED']:
154 raise Error("Attempt to apply configuration from state %s" % self.__state)
156 for child in self.__children:
159 log("Applying changes to %s configuration" % self.__fname)
161 # Remove previous backup.
162 if os.access(self.__oldpath, os.F_OK):
163 os.unlink(self.__oldpath)
165 # Save current configuration.
166 if os.access(self.__path, os.F_OK):
167 os.link(self.__path, self.__oldpath)
168 os.unlink(self.__path)
170 # Apply new configuration.
171 assert(os.path.exists(self.__newpath))
172 if not self.__unlink:
173 os.link(self.__newpath, self.__path)
175 pass # implicit unlink of original file
177 # Remove temporary file.
178 os.unlink(self.__newpath)
180 self.__state = self.__STATE['APPLIED']
183 if self.__state != self.__STATE['APPLIED']:
184 raise Error("Attempt to revert configuration from state %s" % self.__state)
186 for child in self.__children:
189 log("Reverting changes to %s configuration" % self.__fname)
191 # Remove existing new configuration
192 if os.access(self.__newpath, os.F_OK):
193 os.unlink(self.__newpath)
195 # Revert new configuration.
196 if os.access(self.__path, os.F_OK):
197 os.link(self.__path, self.__newpath)
198 os.unlink(self.__path)
200 # Revert to old configuration.
201 if os.access(self.__oldpath, os.F_OK):
202 os.link(self.__oldpath, self.__path)
203 os.unlink(self.__oldpath)
205 # Leave .*.xapi-new as an aid to debugging.
207 self.__state = self.__STATE['REVERTED']
210 if self.__state != self.__STATE['APPLIED']:
211 raise Error("Attempt to commit configuration from state %s" % self.__state)
213 for child in self.__children:
216 log("Committing changes to %s configuration" % self.__fname)
218 if os.access(self.__oldpath, os.F_OK):
219 os.unlink(self.__oldpath)
220 if os.access(self.__newpath, os.F_OK):
221 os.unlink(self.__newpath)
223 self.__state = self.__STATE['COMMITTED']
226 return output_directory is not None
230 print >>sys.stderr, s
234 def check_allowed(pif):
235 pifrec = db.get_pif_record(pif)
237 f = open("/proc/ardence")
238 macline = filter(lambda x: x.startswith("HWaddr:"), f.readlines())
240 if len(macline) == 1:
241 p = re.compile(".*\s%(MAC)s\s.*" % pifrec, re.IGNORECASE)
242 if p.match(macline[0]):
243 log("Skipping PVS device %(device)s (%(MAC)s)" % pifrec)
249 def interface_exists(i):
250 return os.path.exists("/sys/class/net/" + i)
252 def get_netdev_mac(device):
254 return read_first_line_of_file("/sys/class/net/%s/address" % device)
256 # Probably no such device.
259 def get_netdev_tx_queue_len(device):
261 return int(read_first_line_of_file("/sys/class/net/%s/tx_queue_len"
264 # Probably no such device.
267 def get_netdev_by_mac(mac):
269 for device in os.listdir("/sys/class/net"):
270 dev_mac = get_netdev_mac(device)
271 if dev_mac and mac.lower() == dev_mac.lower():
272 if get_netdev_tx_queue_len(device):
275 # Probably a datapath internal port.
279 class DatabaseCache(object):
280 def __init__(self, session_ref=None, cache_file=None):
281 if session_ref and cache_file:
282 raise Error("can't specify session reference and cache file")
284 if cache_file == None:
285 session = XenAPI.xapi_local()
288 log("No session ref given on command line, logging in.")
289 session.xenapi.login_with_password("root", "")
291 session._session = session_ref
294 self.__vlans = session.xenapi.VLAN.get_all_records()
295 self.__bonds = session.xenapi.Bond.get_all_records()
296 self.__pifs = session.xenapi.PIF.get_all_records()
297 self.__networks = session.xenapi.network.get_all_records()
300 session.xenapi.session.logout()
302 log("Loading xapi database cache from %s" % cache_file)
303 f = open(cache_file, 'r')
304 members = pickle.load(f)
305 self.extras = pickle.load(f)
308 self.__vlans = members['vlans']
309 self.__bonds = members['bonds']
310 self.__pifs = members['pifs']
311 self.__networks = members['networks']
313 def save(self, cache_file, extras):
314 f = open(cache_file, 'w')
315 pickle.dump({'vlans': self.__vlans,
316 'bonds': self.__bonds,
318 'networks': self.__networks}, f)
319 pickle.dump(extras, f)
322 def get_pif_by_uuid(self, uuid):
323 pifs = map(lambda (ref,rec): ref,
324 filter(lambda (ref,rec): uuid == rec['uuid'],
325 self.__pifs.items()))
327 raise Error("Unknown PIF \"%s\"" % uuid)
329 raise Error("Non-unique PIF \"%s\"" % uuid)
333 def get_pifs_by_record(self, record):
334 """record is partial pif record.
335 Get the pif(s) whose record matches.
339 if record[key] != pifrec[key]:
343 return map(lambda (ref,rec): ref,
344 filter(lambda (ref,rec): match(rec),
345 self.__pifs.items()))
347 def get_pif_by_record(self, record):
348 """record is partial pif record.
349 Get the pif whose record matches.
351 pifs = self.get_pifs_by_record(record)
353 raise Error("No matching PIF \"%s\"" % str(record))
355 raise Error("Multiple matching PIFs \"%s\"" % str(record))
359 def get_pif_by_bridge(self, host, bridge):
360 networks = map(lambda (ref,rec): ref,
361 filter(lambda (ref,rec): rec['bridge'] == bridge,
362 self.__networks.items()))
363 if len(networks) == 0:
364 raise Error("No matching network \"%s\"")
367 for network in networks:
368 nwrec = self.get_network_record(network)
369 for pif in nwrec['PIFs']:
370 pifrec = self.get_pif_record(pif)
371 if pifrec['host'] != host:
374 raise Error("Multiple PIFs on %s for network %s" % (host, bridge))
377 raise Error("No PIF on %s for network %s" % (host, bridge))
380 def get_pif_record(self, pif):
381 if self.__pifs.has_key(pif):
382 return self.__pifs[pif]
383 raise Error("Unknown PIF \"%s\"" % pif)
384 def get_all_pifs(self):
386 def pif_exists(self, pif):
387 return self.__pifs.has_key(pif)
389 def get_management_pif(self, host):
390 """ Returns the management pif on host
392 all = self.get_all_pifs()
394 pifrec = self.get_pif_record(pif)
395 if pifrec['management'] and pifrec['host'] == host :
399 def get_network_record(self, network):
400 if self.__networks.has_key(network):
401 return self.__networks[network]
402 raise Error("Unknown network \"%s\"" % network)
403 def get_all_networks(self):
404 return self.__networks
406 def get_bond_record(self, bond):
407 if self.__bonds.has_key(bond):
408 return self.__bonds[bond]
412 def get_vlan_record(self, vlan):
413 if self.__vlans.has_key(vlan):
414 return self.__vlans[vlan]
418 def bridge_name(pif):
419 """Return the bridge name associated with pif, or None if network is bridgeless"""
420 pifrec = db.get_pif_record(pif)
421 nwrec = db.get_network_record(pifrec['network'])
424 # TODO: sanity check that nwrec['bridgeless'] != 'true'
425 return nwrec['bridge']
427 # TODO: sanity check that nwrec['bridgeless'] == 'true'
430 def interface_name(pif):
431 """Construct an interface name from the given PIF record."""
433 pifrec = db.get_pif_record(pif)
435 if pifrec['VLAN'] == '-1':
436 return pifrec['device']
438 return "%(device)s.%(VLAN)s" % pifrec
440 def datapath_name(pif):
441 """Return the OpenFlow datapath name associated with pif.
442 For a non-VLAN PIF, the datapath name is the bridge name.
443 For a VLAN PIF, the datapath name is the bridge name for the PIF's VLAN slave.
444 (xapi will create a datapath named with the bridge name even though we won't
448 pifrec = db.get_pif_record(pif)
450 if pifrec['VLAN'] == '-1':
451 return bridge_name(pif)
453 return bridge_name(get_vlan_slave_of_pif(pif))
456 """Return the the name of the network device that carries the
457 IP configuration (if any) associated with pif.
458 The ipdev name is the same as the bridge name.
461 pifrec = db.get_pif_record(pif)
462 return bridge_name(pif)
464 def get_physdev_pifs(pif):
465 """Return the PIFs for the physical network device(s) associated with pif.
466 For a VLAN PIF, this is the VLAN slave's physical device PIF.
467 For a bond master PIF, these are the bond slave PIFs.
468 For a non-VLAN, non-bond master PIF, the PIF is its own physical device PIF.
471 pifrec = db.get_pif_record(pif)
473 if pifrec['VLAN'] != '-1':
474 return [get_vlan_slave_of_pif(pif)]
475 elif len(pifrec['bond_master_of']) != 0:
476 return get_bond_slaves_of_pif(pif)
480 def get_physdev_names(pif):
481 """Return the name(s) of the physical network device(s) associated with pif.
482 For a VLAN PIF, the physical devices are the VLAN slave's physical devices.
483 For a bond master PIF, the physical devices are the bond slaves.
484 For a non-VLAN, non-bond master PIF, the physical device is the PIF itself.
487 return [db.get_pif_record(phys)['device'] for phys in get_physdev_pifs(pif)]
489 def log_pif_action(action, pif):
490 pifrec = db.get_pif_record(pif)
491 pifrec['action'] = action
492 pifrec['interface-name'] = interface_name(pif)
493 if action == "rewrite":
494 pifrec['message'] = "Rewrite PIF %(uuid)s configuration" % pifrec
496 pifrec['message'] = "Bring %(action)s PIF %(uuid)s" % pifrec
497 log("%(message)s: %(interface-name)s configured as %(ip_configuration_mode)s" % pifrec)
499 def get_bond_masters_of_pif(pif):
500 """Returns a list of PIFs which are bond masters of this PIF"""
502 pifrec = db.get_pif_record(pif)
504 bso = pifrec['bond_slave_of']
506 # bond-slave-of is currently a single reference but in principle a
507 # PIF could be a member of several bonds which are not
508 # concurrently attached. Be robust to this possibility.
509 if not bso or bso == "OpaqueRef:NULL":
511 elif not type(bso) == list:
514 bondrecs = [db.get_bond_record(bond) for bond in bso]
515 bondrecs = [rec for rec in bondrecs if rec]
517 return [bond['master'] for bond in bondrecs]
519 def get_bond_slaves_of_pif(pif):
520 """Returns a list of PIFs which make up the given bonded pif."""
522 pifrec = db.get_pif_record(pif)
523 host = pifrec['host']
525 bmo = pifrec['bond_master_of']
527 raise Error("Bond-master-of contains too many elements")
532 bondrec = db.get_bond_record(bmo[0])
534 raise Error("No bond record for bond master PIF")
536 return bondrec['slaves']
538 def get_vlan_slave_of_pif(pif):
539 """Find the PIF which is the VLAN slave of pif.
541 Returns the 'physical' PIF underneath the a VLAN PIF @pif."""
543 pifrec = db.get_pif_record(pif)
545 vlan = pifrec['VLAN_master_of']
546 if not vlan or vlan == "OpaqueRef:NULL":
547 raise Error("PIF is not a VLAN master")
549 vlanrec = db.get_vlan_record(vlan)
551 raise Error("No VLAN record found for PIF")
553 return vlanrec['tagged_PIF']
555 def get_vlan_masters_of_pif(pif):
556 """Returns a list of PIFs which are VLANs on top of the given pif."""
558 pifrec = db.get_pif_record(pif)
559 vlans = [db.get_vlan_record(v) for v in pifrec['VLAN_slave_of']]
560 return [v['untagged_PIF'] for v in vlans if v and db.pif_exists(v['untagged_PIF'])]
562 def interface_deconfigure_commands(interface):
563 # The use of [!0-9] keeps an interface of 'eth0' from matching
564 # VLANs attached to eth0 (such as 'eth0.123'), which are distinct
566 return ['--del-match=bridge.*.port=%s' % interface,
567 '--del-match=bonding.%s.[!0-9]*' % interface,
568 '--del-match=bonding.*.slave=%s' % interface,
569 '--del-match=vlan.%s.[!0-9]*' % interface,
570 '--del-match=port.%s.[!0-9]*' % interface,
571 '--del-match=iface.%s.[!0-9]*' % interface]
573 def run_command(command):
574 log("Running command: " + ' '.join(command))
575 if os.spawnl(os.P_WAIT, command[0], *command) != 0:
576 log("Command failed: " + ' '.join(command))
580 def rename_netdev(old_name, new_name):
581 log("Changing the name of %s to %s" % (old_name, new_name))
582 run_command(['/sbin/ifconfig', old_name, 'down'])
583 if not run_command(['/sbin/ip', 'link', 'set', old_name,
585 raise Error("Could not rename %s to %s" % (old_name, new_name))
587 # Check whether 'pif' exists and has the correct MAC.
588 # If not, try to find a device with the correct MAC and rename it.
589 # 'already_renamed' is used to avoid infinite recursion.
590 def remap_pif(pif, already_renamed=[]):
591 pifrec = db.get_pif_record(pif)
592 device = pifrec['device']
595 # Is there a network device named 'device' at all?
596 device_exists = interface_exists(device)
598 # Yes. Does it have MAC 'mac'?
599 found_mac = get_netdev_mac(device)
600 if found_mac and mac.lower() == found_mac.lower():
601 # Yes, everything checks out the way we want. Nothing to do.
604 log("No network device %s" % device)
606 # What device has MAC 'mac'?
607 cur_device = get_netdev_by_mac(mac)
609 log("No network device has MAC %s" % mac)
612 # First rename 'device', if it exists, to get it out of the way
613 # for 'cur_device' to replace it.
615 rename_netdev(device, "dev%d" % random.getrandbits(24))
617 # Rename 'cur_device' to 'device'.
618 rename_netdev(cur_device, device)
620 def read_first_line_of_file(name):
623 file = open(name, 'r')
624 return file.readline().rstrip('\n')
629 def down_netdev(interface, deconfigure=True):
630 if not interface_exists(interface):
631 log("down_netdev: interface %s does not exist, ignoring" % interface)
635 pidfile_name = '/var/run/dhclient-%s.pid' % interface
637 os.kill(int(read_first_line_of_file(pidfile_name)), signal.SIGTERM)
641 # Remove dhclient pidfile.
643 os.remove(pidfile_name)
647 run_command(["/sbin/ifconfig", interface, '0.0.0.0'])
649 run_command(["/sbin/ifconfig", interface, 'down'])
651 def up_netdev(interface):
652 run_command(["/sbin/ifconfig", interface, 'up'])
654 def find_distinguished_pifs(pif):
655 """Returns the PIFs on host that own DNS and the default route.
656 The peerdns pif will be the one with pif::other-config:peerdns=true, or the mgmt pif if none have this set.
657 The gateway pif will be the one with pif::other-config:defaultroute=true, or the mgmt pif if none have this set.
659 Note: we prune out the bond master pif (if it exists).
660 This is because when we are called to bring up an interface with a bond master, it is implicit that
661 we should bring down that master."""
663 pifrec = db.get_pif_record(pif)
664 host = pifrec['host']
666 pifs_on_host = [ __pif for __pif in db.get_all_pifs() if
667 db.get_pif_record(__pif)['host'] == host and
668 (not __pif in get_bond_masters_of_pif(pif)) ]
671 defaultroute_pif = None
673 # loop through all the pifs on this host looking for one with
674 # other-config:peerdns = true, and one with
675 # other-config:default-route=true
676 for __pif in pifs_on_host:
677 __pifrec = db.get_pif_record(__pif)
678 __oc = __pifrec['other_config']
679 if __oc.has_key('peerdns') and __oc['peerdns'] == 'true':
680 if peerdns_pif == None:
683 log('Warning: multiple pifs with "peerdns=true" - choosing %s and ignoring %s' % \
684 (db.get_pif_record(peerdns_pif)['device'], __pifrec['device']))
685 if __oc.has_key('defaultroute') and __oc['defaultroute'] == 'true':
686 if defaultroute_pif == None:
687 defaultroute_pif = __pif
689 log('Warning: multiple pifs with "defaultroute=true" - choosing %s and ignoring %s' % \
690 (db.get_pif_record(defaultroute_pif)['device'], __pifrec['device']))
692 # If no pif is explicitly specified then use the mgmt pif for peerdns/defaultroute
693 if peerdns_pif == None:
694 peerdns_pif = management_pif
695 if defaultroute_pif == None:
696 defaultroute_pif = management_pif
698 return peerdns_pif, defaultroute_pif
700 def run_ethtool(device, oc):
701 # Run "ethtool -s" if there are any settings.
703 if oc.has_key('ethtool-speed'):
704 val = oc['ethtool-speed']
705 if val in ["10", "100", "1000"]:
706 settings += ['speed', val]
708 log("Invalid value for ethtool-speed = %s. Must be 10|100|1000." % val)
709 if oc.has_key('ethtool-duplex'):
710 val = oc['ethtool-duplex']
711 if val in ["10", "100", "1000"]:
712 settings += ['duplex', 'val']
714 log("Invalid value for ethtool-duplex = %s. Must be half|full." % val)
715 if oc.has_key('ethtool-autoneg'):
716 val = oc['ethtool-autoneg']
717 if val in ["true", "on"]:
718 settings += ['autoneg', 'on']
719 elif val in ["false", "off"]:
720 settings += ['autoneg', 'off']
722 log("Invalid value for ethtool-autoneg = %s. Must be on|true|off|false." % val)
724 run_command(['/sbin/ethtool', '-s', device] + settings)
726 # Run "ethtool -K" if there are any offload settings.
728 for opt in ("rx", "tx", "sg", "tso", "ufo", "gso"):
729 if oc.has_key("ethtool-" + opt):
730 val = oc["ethtool-" + opt]
731 if val in ["true", "on"]:
732 offload += [opt, 'on']
733 elif val in ["false", "off"]:
734 offload += [opt, 'off']
736 log("Invalid value for ethtool-%s = %s. Must be on|true|off|false." % (opt, val))
738 run_command(['/sbin/ethtool', '-K', device] + offload)
741 if oc.has_key('mtu'):
743 int(oc['mtu']) # Check that the value is an integer
744 return ['mtu', oc['mtu']]
745 except ValueError, x:
746 log("Invalid value for mtu = %s" % mtu)
749 def configure_netdev(pif):
750 pifrec = db.get_pif_record(pif)
751 datapath = datapath_name(pif)
752 ipdev = ipdev_name(pif)
754 host = pifrec['host']
755 nw = pifrec['network']
756 nwrec = db.get_network_record(nw)
758 pif_oc = pifrec['other_config']
759 nw_oc = nwrec['other_config']
761 # IP (except DHCP) and MTU.
762 ifconfig_argv = ['/sbin/ifconfig', ipdev, 'up']
764 if pifrec['ip_configuration_mode'] == "DHCP":
766 elif pifrec['ip_configuration_mode'] == "Static":
767 ifconfig_argv += [pifrec['IP']]
768 ifconfig_argv += ['netmask', pifrec['netmask']]
769 gateway = pifrec['gateway']
770 elif pifrec['ip_configuration_mode'] == "None":
774 raise Error("Unknown IP-configuration-mode %s" % pifrec['ip_configuration_mode'])
775 ifconfig_argv += mtu_setting(nw_oc)
776 run_command(ifconfig_argv)
778 (peerdns_pif, defaultroute_pif) = find_distinguished_pifs(pif)
781 if peerdns_pif == pif:
782 f = ConfigurationFile('resolv.conf', "/etc")
783 if pif_oc.has_key('domain'):
784 f.write("search %s\n" % pif_oc['domain'])
785 for dns in pifrec['DNS'].split(","):
786 f.write("nameserver %s\n" % dns)
792 if defaultroute_pif == pif and gateway != '':
793 run_command(['/sbin/ip', 'route', 'replace', 'default',
794 'via', gateway, 'dev', ipdev])
795 if nw_oc.has_key('static-routes'):
796 for line in nw_oc['static-routes'].split(','):
797 network, masklen, gateway = line.split('/')
798 run_command(['/sbin/ip', 'route', 'add',
799 '%s/%s' % (network, masklen), 'via', gateway,
803 run_ethtool(ipdev, nw_oc)
806 if pifrec['ip_configuration_mode'] == "DHCP":
808 print "Determining IP information for %s..." % ipdev,
809 argv = ['/sbin/dhclient', '-q',
810 '-lf', '/var/lib/dhclient/dhclient-%s.leases' % ipdev,
811 '-pf', '/var/run/dhclient-%s.pid' % ipdev,
813 if run_command(argv):
818 def modify_config(commands):
819 run_command(['/root/vswitch/bin/ovs-cfg-mod', '-vANY:console:emer',
820 '-F', '/etc/ovs-vswitchd.conf']
822 run_command(['/sbin/service', 'vswitch', 'reload'])
824 def is_bond_pif(pif):
825 pifrec = db.get_pif_record(pif)
826 return len(pifrec['bond_master_of']) != 0
828 def configure_bond(pif):
829 pifrec = db.get_pif_record(pif)
830 interface = interface_name(pif)
831 ipdev = ipdev_name(pif)
832 datapath = datapath_name(pif)
833 physdev_names = get_physdev_names(pif)
835 argv = ['--del-match=bonding.%s.[!0-9]*' % interface]
836 argv += ["--add=bonding.%s.slave=%s" % (interface, slave)
837 for slave in physdev_names]
838 argv += ['--add=bonding.%s.fake-iface=true']
840 if pifrec['MAC'] != "":
841 argv += ['--add=port.%s.mac=%s' % (interface, pifrec['MAC'])]
845 "mode": "balance-slb",
851 # override defaults with values from other-config whose keys
853 oc = pifrec['other_config']
854 overrides = filter(lambda (key,val):
855 key.startswith("bond-"), oc.items())
856 overrides = map(lambda (key,val): (key[5:], val), overrides)
857 bond_options.update(overrides)
858 for (name,val) in bond_options.items():
859 argv += ["--add=bonding.%s.%s=%s" % (interface, name, val)]
863 pifrec = db.get_pif_record(pif)
865 bridge = bridge_name(pif)
866 interface = interface_name(pif)
867 ipdev = ipdev_name(pif)
868 datapath = datapath_name(pif)
869 physdev_pifs = get_physdev_pifs(pif)
870 physdev_names = get_physdev_names(pif)
872 if pifrec['VLAN'] != '-1':
873 vlan_slave = get_vlan_slave_of_pif(pif)
874 if vlan_slave and is_bond_pif(vlan_slave):
875 bond_master = vlan_slave
876 elif is_bond_pif(pif):
881 bond_slaves = get_bond_slaves_of_pif(bond_master)
884 bond_masters = get_bond_masters_of_pif(pif)
886 # Support "rpm -e vswitch" gracefully by keeping Centos configuration
887 # files up-to-date, even though we don't use them or need them.
888 f = configure_pif(pif)
889 mode = pifrec['ip_configuration_mode']
891 log("Configuring %s using %s configuration" % (bridge, mode))
892 br = open_network_ifcfg(pif)
893 configure_network(pif, br)
897 log("Configuring %s using %s configuration" % (interface, mode))
898 configure_network(pif, f)
900 for master in bond_masters:
901 master_bridge = bridge_name(master)
902 removed = unconfigure_pif(master)
903 f.attach_child(removed)
905 removed = open_network_ifcfg(master)
906 log("Unlinking stale file %s" % removed.path())
908 f.attach_child(removed)
910 # /etc/xensource/scripts/vif needs to know where to add VIFs.
912 if not os.path.exists(vswitch_config_dir):
913 os.mkdir(vswitch_config_dir)
914 br = ConfigurationFile("br-%s" % bridge, vswitch_config_dir)
915 br.write("VLAN_SLAVE=%s\n" % datapath)
916 br.write("VLAN_VID=%s\n" % pifrec['VLAN'])
920 # Update all configuration files (both ours and Centos's).
924 # Check the MAC address of each network device and remap if
925 # necessary to make names match our expectations.
926 for physdev_pif in physdev_pifs:
927 remap_pif(physdev_pif)
929 # "ifconfig down" the network device and delete its IP address, etc.
931 for physdev_name in physdev_names:
932 down_netdev(physdev_name)
934 # If we are bringing up a bond, remove IP addresses from the
935 # slaves (because we are implicitly being asked to take them down).
937 # Conversely, if we are bringing up an interface that has bond
938 # masters, remove IP addresses from the bond master (because we
939 # are implicitly being asked to take it down).
940 for bond_pif in bond_slaves + bond_masters:
941 run_command(["/sbin/ifconfig", ipdev_name(bond_pif), '0.0.0.0'])
943 # Remove all keys related to pif and any bond masters linked to PIF.
944 del_ports = [ipdev] + physdev_names + bond_masters
945 if vlan_slave and bond_master:
946 del_ports += [interface_name(bond_master)]
948 # What ports do we need to add to the datapath?
950 # We definitely need the ipdev, and ordinarily we want the
951 # physical devices too, but for bonds we need the bond as bridge
953 add_ports = [ipdev, datapath]
955 add_ports += physdev_names
957 add_ports += [interface_name(bond_master)]
959 # What ports do we need to delete?
961 # - All the ports that we add, to avoid duplication and to drop
962 # them from another datapath in case they're misassigned.
964 # - The physical devices, since they will either be in add_ports
965 # or added to the bonding device (see below).
967 # - The bond masters for pif. (Ordinarily pif shouldn't have any
968 # bond masters. If it does then interface-reconfigure is
969 # implicitly being asked to take them down.)
970 del_ports = add_ports + physdev_names + bond_masters
972 # What networks does this datapath carry?
974 # - The network corresponding to the datapath's PIF.
976 # - The networks corresponding to any VLANs attached to the
979 for nwpif in db.get_pifs_by_record({'device': pifrec['device'],
980 'host': pifrec['host']}):
981 net = db.get_pif_record(nwpif)['network']
982 network_uuids += [db.get_network_record(net)['uuid']]
984 # Bring up bond slaves early, because ovs-vswitchd initially
985 # enables or disables bond slaves based on whether carrier is
986 # detected when they are added, and a network device that is down
987 # always reports "no carrier".
988 bond_slave_physdev_names = []
989 for slave in bond_slaves:
990 bond_slave_physdev_names += physdev_names(slave)
991 for slave_physdev_name in bond_slave_physdev_names:
992 up_netdev(slave_physdev_name)
994 # Now modify the ovs-vswitchd config file.
996 for port in set(del_ports):
997 argv += interface_deconfigure_commands(port)
998 for port in set(add_ports):
999 argv += ['--add=bridge.%s.port=%s' % (datapath, port)]
1001 argv += ['--add=vlan.%s.tag=%s' % (ipdev, pifrec['VLAN'])]
1002 argv += ['--add=iface.%s.internal=true' % (ipdev)]
1004 # xapi creates a bridge by the name of the ipdev and requires
1005 # that the IP address will be on it. We need to delete this
1006 # bridge because we need that device to be a member of our
1008 argv += ['--del-match=bridge.%s.[!0-9]*' % ipdev]
1010 # xapi insists that its attempts to create the bridge succeed,
1011 # so force that to happen.
1012 argv += ['--add=iface.%s.fake-bridge=true' % (ipdev)]
1015 os.unlink("%s/br-%s" % (vswitch_config_dir, bridge))
1018 argv += ['--del-match=bridge.%s.xs-network-uuids=*' % datapath]
1019 argv += ['--add=bridge.%s.xs-network-uuids=%s' % (datapath, uuid)
1020 for uuid in set(network_uuids)]
1022 argv += configure_bond(bond_master)
1025 # Bring up VLAN slave, plus physical devices other than bond
1026 # slaves (which we brought up earlier).
1028 up_netdev(ipdev_name(vlan_slave))
1029 for physdev_name in set(physdev_names) - set(bond_slave_physdev_names):
1030 up_netdev(physdev_name)
1032 # Configure network devices.
1033 configure_netdev(pif)
1035 # Update /etc/issue (which contains the IP address of the management interface)
1036 os.system("/sbin/update-issue")
1039 # There seems to be a race somewhere: without this sleep, using
1040 # XenCenter to create a bond that becomes the management interface
1041 # fails with "The underlying connection was closed: A connection that
1042 # was expected to be kept alive was closed by the server." on every
1043 # second or third try, even though /var/log/messages doesn't show
1046 # The race is probably present even without vswitch, but bringing up a
1047 # bond without vswitch involves a built-in pause of 10 seconds or more
1048 # to wait for the bond to transition from learning to forwarding state.
1051 def action_down(pif):
1052 rec = db.get_pif_record(pif)
1053 interface = interface_name(pif)
1054 bridge = bridge_name(pif)
1055 ipdev = ipdev_name(pif)
1057 # Support "rpm -e vswitch" gracefully by keeping Centos configuration
1058 # files up-to-date, even though we don't use them or need them.
1059 f = unconfigure_pif(pif)
1061 br = open_network_ifcfg(pif)
1062 log("Unlinking stale file %s" % br.path())
1069 log("action_down failed to apply changes: %s" % e.msg)
1074 if rec['VLAN'] != '-1':
1075 # Get rid of the VLAN device itself.
1077 argv += interface_deconfigure_commands(ipdev)
1079 # If the VLAN's slave is attached, stop here.
1080 slave = get_vlan_slave_of_pif(pif)
1081 if db.get_pif_record(slave)['currently_attached']:
1082 log("VLAN slave is currently attached")
1086 # If the VLAN's slave has other VLANs that are attached, stop here.
1087 masters = get_vlan_masters_of_pif(slave)
1089 if m != pif and db.get_pif_record(m)['currently_attached']:
1090 log("VLAN slave has other master %s" % interface_naem(m))
1094 # Otherwise, take down the VLAN's slave too.
1095 log("No more masters, bring down vlan slave %s" % interface_name(slave))
1098 # Stop here if this PIF has attached VLAN masters.
1099 vlan_masters = get_vlan_masters_of_pif(pif)
1100 log("VLAN masters of %s - %s" % (rec['device'], [interface_name(m) for m in vlan_masters]))
1101 for m in vlan_masters:
1102 if db.get_pif_record(m)['currently_attached']:
1103 log("Leaving %s up due to currently attached VLAN master %s" % (interface, interface_name(m)))
1106 # pif is now either a bond or a physical device which needs to be
1107 # brought down. pif might have changed so re-check all its attributes.
1108 rec = db.get_pif_record(pif)
1109 interface = interface_name(pif)
1110 bridge = bridge_name(pif)
1111 ipdev = ipdev_name(pif)
1114 bond_slaves = get_bond_slaves_of_pif(pif)
1115 log("bond slaves of %s - %s" % (rec['device'], [interface_name(s) for s in bond_slaves]))
1116 for slave in bond_slaves:
1117 slave_interface = interface_name(slave)
1118 log("bring down bond slave %s" % slave_interface)
1119 argv += interface_deconfigure_commands(slave_interface)
1120 down_netdev(slave_interface)
1122 argv += interface_deconfigure_commands(ipdev)
1125 argv += ['--del-match', 'bridge.%s.*' % datapath_name(pif)]
1126 argv += ['--del-match', 'bonding.%s.[!0-9]*' % interface]
1129 def action_rewrite(pif):
1130 # Support "rpm -e vswitch" gracefully by keeping Centos configuration
1131 # files up-to-date, even though we don't use them or need them.
1132 pifrec = db.get_pif_record(pif)
1133 f = configure_pif(pif)
1134 interface = interface_name(pif)
1135 bridge = bridge_name(pif)
1136 mode = pifrec['ip_configuration_mode']
1138 log("Configuring %s using %s configuration" % (bridge, mode))
1139 br = open_network_ifcfg(pif)
1140 configure_network(pif, br)
1144 log("Configuring %s using %s configuration" % (interface, mode))
1145 configure_network(pif, f)
1151 log("failed to apply changes: %s" % e.msg)
1155 # We have no code of our own to run here.
1158 def main(argv=None):
1159 global output_directory, management_pif
1165 force_interface = None
1166 force_management = False
1174 longops = [ "output-directory=",
1175 "pif=", "pif-uuid=",
1181 "device=", "mode=", "ip=", "netmask=", "gateway=",
1183 arglist, args = getopt.gnu_getopt(argv[1:], shortops, longops)
1184 except getopt.GetoptError, msg:
1187 force_rewrite_config = {}
1190 if o == "--output-directory":
1191 output_directory = a
1194 elif o == "--pif-uuid":
1196 elif o == "--session":
1198 elif o == "--force-interface" or o == "--force":
1200 elif o == "--management":
1201 force_management = True
1202 elif o in ["--device", "--mode", "--ip", "--netmask", "--gateway"]:
1203 force_rewrite_config[o[2:]] = a
1204 elif o == "-h" or o == "--help":
1205 print __doc__ % {'command-name': os.path.basename(argv[0])}
1208 if not debug_mode():
1209 syslog.openlog(os.path.basename(argv[0]))
1210 log("Called as " + str.join(" ", argv))
1212 raise Usage("Required option <action> not present")
1214 raise Usage("Too many arguments")
1217 # backwards compatibility
1218 if action == "rewrite-configuration": action = "rewrite"
1220 if output_directory and ( session or pif ):
1221 raise Usage("--session/--pif cannot be used with --output-directory")
1222 if ( session or pif ) and pif_uuid:
1223 raise Usage("--session/--pif and --pif-uuid are mutually exclusive.")
1224 if ( session and not pif ) or ( not session and pif ):
1225 raise Usage("--session and --pif must be used together.")
1226 if force_interface and ( session or pif or pif_uuid ):
1227 raise Usage("--force is mutually exclusive with --session, --pif and --pif-uuid")
1228 if len(force_rewrite_config) and not (force_interface and action == "rewrite"):
1229 raise Usage("\"--force rewrite\" needed for --device, --mode, --ip, --netmask, and --gateway")
1233 log("Force interface %s %s" % (force_interface, action))
1235 if action == "rewrite":
1236 action_force_rewrite(force_interface, force_rewrite_config)
1238 db = DatabaseCache(cache_file=dbcache_file)
1239 host = db.extras['host']
1240 pif = db.get_pif_by_bridge(host, force_interface)
1241 management_pif = db.get_management_pif(host)
1245 elif action == "down":
1248 raise Usage("Unknown action %s" % action)
1250 db = DatabaseCache(session_ref=session)
1253 pif = db.get_pif_by_uuid(pif_uuid)
1256 raise Usage("No PIF given")
1258 if force_management:
1259 # pif is going to be the management pif
1260 management_pif = pif
1262 # pif is not going to be the management pif.
1263 # Search DB cache for pif on same host with management=true
1264 pifrec = db.get_pif_record(pif)
1265 host = pifrec['host']
1266 management_pif = db.get_management_pif(host)
1268 log_pif_action(action, pif)
1270 if not check_allowed(pif):
1275 elif action == "down":
1277 elif action == "rewrite":
1280 raise Usage("Unknown action %s" % action)
1283 pifrec = db.get_pif_record(pif)
1284 db.save(dbcache_file, {'host': pifrec['host']})
1287 print >>sys.stderr, err.msg
1288 print >>sys.stderr, "For help use --help."
1296 # The following code allows interface-reconfigure to keep Centos
1297 # network configuration files up-to-date, even though the vswitch
1298 # never uses them. In turn, that means that "rpm -e vswitch" does not
1299 # have to update any configuration files.
1301 def configure_ethtool(oc, f):
1302 # Options for "ethtool -s"
1304 setting_opts = ["autoneg", "speed", "duplex"]
1305 # Options for "ethtool -K"
1307 offload_opts = ["rx", "tx", "sg", "tso", "ufo", "gso"]
1309 for opt in [opt for opt in setting_opts + offload_opts if oc.has_key("ethtool-" + opt)]:
1310 val = oc["ethtool-" + opt]
1312 if opt in ["speed"]:
1313 if val in ["10", "100", "1000"]:
1314 val = "speed " + val
1316 log("Invalid value for ethtool-speed = %s. Must be 10|100|1000." % val)
1318 elif opt in ["duplex"]:
1319 if val in ["half", "full"]:
1320 val = "duplex " + val
1322 log("Invalid value for ethtool-duplex = %s. Must be half|full." % val)
1324 elif opt in ["autoneg"] + offload_opts:
1325 if val in ["true", "on"]:
1327 elif val in ["false", "off"]:
1330 log("Invalid value for ethtool-%s = %s. Must be on|true|off|false." % (opt, val))
1333 if opt in setting_opts:
1334 if val and settings:
1335 settings = settings + " " + val
1338 elif opt in offload_opts:
1340 offload = offload + " " + val
1345 f.write("ETHTOOL_OPTS=\"%s\"\n" % settings)
1347 f.write("ETHTOOL_OFFLOAD_OPTS=\"%s\"\n" % offload)
1349 def configure_mtu(oc, f):
1350 if not oc.has_key('mtu'):
1354 mtu = int(oc['mtu'])
1355 f.write("MTU=%d\n" % mtu)
1356 except ValueError, x:
1357 log("Invalid value for mtu = %s" % mtu)
1359 def configure_static_routes(interface, oc, f):
1360 """Open a route-<interface> file for static routes.
1362 Opens the static routes configuration file for interface and writes one
1363 line for each route specified in the network's other config "static-routes" value.
1365 interface ( RO): xenbr1
1366 other-config (MRW): static-routes: 172.16.0.0/15/192.168.0.3,172.18.0.0/16/192.168.0.4;...
1368 Then route-xenbr1 should be
1369 172.16.0.0/15 via 192.168.0.3 dev xenbr1
1370 172.18.0.0/16 via 192.168.0.4 dev xenbr1
1372 fname = "route-%s" % interface
1373 if oc.has_key('static-routes'):
1374 # The key is present - extract comma seperates entries
1375 lines = oc['static-routes'].split(',')
1377 # The key is not present, i.e. there are no static routes
1380 child = ConfigurationFile(fname)
1381 child.write("# DO NOT EDIT: This file (%s) was autogenerated by %s\n" % \
1382 (os.path.basename(child.path()), os.path.basename(sys.argv[0])))
1386 network, masklen, gateway = l.split('/')
1387 child.write("%s/%s via %s dev %s\n" % (network, masklen, gateway, interface))
1389 f.attach_child(child)
1392 except ValueError, e:
1393 log("Error in other-config['static-routes'] format for network %s: %s" % (interface, e))
1395 def __open_ifcfg(interface):
1396 """Open a network interface configuration file.
1398 Opens the configuration file for interface, writes a header and
1399 common options and returns the file object.
1401 fname = "ifcfg-%s" % interface
1402 f = ConfigurationFile(fname)
1404 f.write("# DO NOT EDIT: This file (%s) was autogenerated by %s\n" % \
1405 (os.path.basename(f.path()), os.path.basename(sys.argv[0])))
1406 f.write("XEMANAGED=yes\n")
1407 f.write("DEVICE=%s\n" % interface)
1408 f.write("ONBOOT=no\n")
1412 def open_network_ifcfg(pif):
1413 bridge = bridge_name(pif)
1414 interface = interface_name(pif)
1416 return __open_ifcfg(bridge)
1418 return __open_ifcfg(interface)
1421 def open_pif_ifcfg(pif):
1422 pifrec = db.get_pif_record(pif)
1424 log("Configuring %s (%s)" % (interface_name(pif), pifrec['MAC']))
1426 f = __open_ifcfg(interface_name(pif))
1428 if pifrec.has_key('other_config'):
1429 configure_ethtool(pifrec['other_config'], f)
1430 configure_mtu(pifrec['other_config'], f)
1434 def configure_network(pif, f):
1435 """Write the configuration file for a network.
1437 Writes configuration derived from the network object into the relevant
1438 ifcfg file. The configuration file is passed in, but if the network is
1439 bridgeless it will be ifcfg-<interface>, otherwise it will be ifcfg-<bridge>.
1441 This routine may also write ifcfg files of the networks corresponding to other PIFs
1442 in order to maintain consistency.
1445 pif: Opaque_ref of pif
1446 f : ConfigurationFile(/path/to/ifcfg) to which we append network configuration
1449 pifrec = db.get_pif_record(pif)
1450 host = pifrec['host']
1451 nw = pifrec['network']
1452 nwrec = db.get_network_record(nw)
1454 bridge = bridge_name(pif)
1455 interface = interface_name(pif)
1461 if nwrec.has_key('other_config'):
1462 configure_ethtool(nwrec['other_config'], f)
1463 configure_mtu(nwrec['other_config'], f)
1464 configure_static_routes(device, nwrec['other_config'], f)
1467 if pifrec.has_key('other_config'):
1468 oc = pifrec['other_config']
1470 if device == bridge:
1471 f.write("TYPE=Bridge\n")
1472 f.write("DELAY=0\n")
1473 f.write("STP=off\n")
1474 f.write("PIFDEV=%s\n" % interface_name(pif))
1476 if pifrec['ip_configuration_mode'] == "DHCP":
1477 f.write("BOOTPROTO=dhcp\n")
1478 f.write("PERSISTENT_DHCLIENT=yes\n")
1479 elif pifrec['ip_configuration_mode'] == "Static":
1480 f.write("BOOTPROTO=none\n")
1481 f.write("NETMASK=%(netmask)s\n" % pifrec)
1482 f.write("IPADDR=%(IP)s\n" % pifrec)
1483 f.write("GATEWAY=%(gateway)s\n" % pifrec)
1484 elif pifrec['ip_configuration_mode'] == "None":
1485 f.write("BOOTPROTO=none\n")
1487 raise Error("Unknown ip-configuration-mode %s" % pifrec['ip_configuration_mode'])
1489 if pifrec.has_key('DNS') and pifrec['DNS'] != "":
1490 ServerList = pifrec['DNS'].split(",")
1491 for i in range(len(ServerList)): f.write("DNS%d=%s\n" % (i+1, ServerList[i]))
1492 if oc and oc.has_key('domain'):
1493 f.write("DOMAIN='%s'\n" % oc['domain'].replace(',', ' '))
1495 # We only allow one ifcfg-xenbr* to have PEERDNS=yes and there can be only one GATEWAYDEV in /etc/sysconfig/network.
1496 # The peerdns pif will be the one with pif::other-config:peerdns=true, or the mgmt pif if none have this set.
1497 # The gateway pif will be the one with pif::other-config:defaultroute=true, or the mgmt pif if none have this set.
1499 # Work out which pif on this host should be the one with PEERDNS=yes and which should be the GATEWAYDEV
1501 # Note: we prune out the bond master pif (if it exists).
1502 # This is because when we are called to bring up an interface with a bond master, it is implicit that
1503 # we should bring down that master.
1504 pifs_on_host = [ __pif for __pif in db.get_all_pifs() if
1505 db.get_pif_record(__pif)['host'] == host and
1506 (not __pif in get_bond_masters_of_pif(pif)) ]
1507 other_pifs_on_host = [ __pif for __pif in pifs_on_host if __pif != pif ]
1510 defaultroute_pif = None
1512 # loop through all the pifs on this host looking for one with
1513 # other-config:peerdns = true, and one with
1514 # other-config:default-route=true
1515 for __pif in pifs_on_host:
1516 __pifrec = db.get_pif_record(__pif)
1517 __oc = __pifrec['other_config']
1518 if __oc.has_key('peerdns') and __oc['peerdns'] == 'true':
1519 if peerdns_pif == None:
1522 log('Warning: multiple pifs with "peerdns=true" - choosing %s and ignoring %s' % \
1523 (db.get_pif_record(peerdns_pif)['device'], __pifrec['device']))
1524 if __oc.has_key('defaultroute') and __oc['defaultroute'] == 'true':
1525 if defaultroute_pif == None:
1526 defaultroute_pif = __pif
1528 log('Warning: multiple pifs with "defaultroute=true" - choosing %s and ignoring %s' % \
1529 (db.get_pif_record(defaultroute_pif)['device'], __pifrec['device']))
1531 # If no pif is explicitly specified then use the mgmt pif for peerdns/defaultroute
1532 if peerdns_pif == None:
1533 peerdns_pif = management_pif
1534 if defaultroute_pif == None:
1535 defaultroute_pif = management_pif
1537 # Update all the other network's ifcfg files and ensure consistency
1538 for __pif in other_pifs_on_host:
1539 __f = open_network_ifcfg(__pif)
1540 peerdns_line_wanted = 'PEERDNS=%s\n' % ((__pif == peerdns_pif) and 'yes' or 'no')
1541 lines = __f.readlines()
1543 if not peerdns_line_wanted in lines:
1544 # the PIF selected for DNS has changed and as a result this ifcfg file needs rewriting
1546 if not line.lstrip().startswith('PEERDNS'):
1548 log("Setting %s in %s" % (peerdns_line_wanted.strip(), __f.path()))
1549 __f.write(peerdns_line_wanted)
1554 # There is no need to change this ifcfg file. So don't attach_child.
1557 # ... and for this pif too
1558 f.write('PEERDNS=%s\n' % ((pif == peerdns_pif) and 'yes' or 'no'))
1561 fnetwork = ConfigurationFile("network", "/etc/sysconfig")
1562 for line in fnetwork.readlines():
1563 if line.lstrip().startswith('GATEWAY') :
1565 fnetwork.write(line)
1566 if defaultroute_pif:
1567 gatewaydev = bridge_name(defaultroute_pif)
1569 gatewaydev = interface_name(defaultroute_pif)
1570 fnetwork.write('GATEWAYDEV=%s\n' % gatewaydev)
1572 f.attach_child(fnetwork)
1577 def configure_physical_interface(pif):
1578 """Write the configuration for a physical interface.
1580 Writes the configuration file for the physical interface described by
1583 Returns the open file handle for the interface configuration file.
1586 pifrec = db.get_pif_record(pif)
1588 f = open_pif_ifcfg(pif)
1590 f.write("TYPE=Ethernet\n")
1591 f.write("HWADDR=%(MAC)s\n" % pifrec)
1595 def configure_bond_interface(pif):
1596 """Write the configuration for a bond interface.
1598 Writes the configuration file for the bond interface described by
1599 the pif object. Handles writing the configuration for the slave
1602 Returns the open file handle for the bond interface configuration
1606 pifrec = db.get_pif_record(pif)
1607 oc = pifrec['other_config']
1608 f = open_pif_ifcfg(pif)
1610 if pifrec['MAC'] != "":
1611 f.write("MACADDR=%s\n" % pifrec['MAC'])
1613 for slave in get_bond_slaves_of_pif(pif):
1614 s = configure_physical_interface(slave)
1615 s.write("MASTER=%(device)s\n" % pifrec)
1616 s.write("SLAVE=yes\n")
1620 # The bond option defaults
1622 "mode": "balance-slb",
1629 # override defaults with values from other-config whose keys being with "bond-"
1630 overrides = filter(lambda (key,val): key.startswith("bond-"), oc.items())
1631 overrides = map(lambda (key,val): (key[5:], val), overrides)
1632 bond_options.update(overrides)
1634 # write the bond options to ifcfg-bondX
1635 f.write('BONDING_OPTS="')
1636 for (name,val) in bond_options.items():
1637 f.write("%s=%s " % (name,val))
1641 def configure_vlan_interface(pif):
1642 """Write the configuration for a VLAN interface.
1644 Writes the configuration file for the VLAN interface described by
1645 the pif object. Handles writing the configuration for the master
1646 interface if necessary.
1648 Returns the open file handle for the VLAN interface configuration
1652 slave = configure_pif(get_vlan_slave_of_pif(pif))
1655 f = open_pif_ifcfg(pif)
1656 f.write("VLAN=yes\n")
1657 f.attach_child(slave)
1661 def configure_pif(pif):
1662 """Write the configuration for a PIF object.
1664 Writes the configuration file the PIF and all dependent
1665 interfaces (bond slaves and VLAN masters etc).
1667 Returns the open file handle for the interface configuration file.
1670 pifrec = db.get_pif_record(pif)
1672 if pifrec['VLAN'] != '-1':
1673 f = configure_vlan_interface(pif)
1674 elif len(pifrec['bond_master_of']) != 0:
1675 f = configure_bond_interface(pif)
1677 f = configure_physical_interface(pif)
1679 bridge = bridge_name(pif)
1681 f.write("BRIDGE=%s\n" % bridge)
1685 def unconfigure_pif(pif):
1686 """Clear up the files created by configure_pif"""
1687 f = open_pif_ifcfg(pif)
1688 log("Unlinking stale file %s" % f.path())
1692 if __name__ == "__main__":
1698 err = traceback.format_exception(*ex)
1702 if not debug_mode():