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):
268 for device in os.listdir("/sys/class/net"):
269 dev_mac = get_netdev_mac(device)
270 if (dev_mac and mac.lower() == dev_mac.lower() and
271 get_netdev_tx_queue_len(device)):
275 class DatabaseCache(object):
276 def __init__(self, session_ref=None, cache_file=None):
277 if session_ref and cache_file:
278 raise Error("can't specify session reference and cache file")
280 if cache_file == None:
281 session = XenAPI.xapi_local()
284 log("No session ref given on command line, logging in.")
285 session.xenapi.login_with_password("root", "")
287 session._session = session_ref
290 self.__vlans = session.xenapi.VLAN.get_all_records()
291 self.__bonds = session.xenapi.Bond.get_all_records()
292 self.__pifs = session.xenapi.PIF.get_all_records()
293 self.__networks = session.xenapi.network.get_all_records()
296 session.xenapi.session.logout()
298 log("Loading xapi database cache from %s" % cache_file)
299 f = open(cache_file, 'r')
300 members = pickle.load(f)
301 self.extras = pickle.load(f)
304 self.__vlans = members['vlans']
305 self.__bonds = members['bonds']
306 self.__pifs = members['pifs']
307 self.__networks = members['networks']
309 def save(self, cache_file, extras):
310 f = open(cache_file, 'w')
311 pickle.dump({'vlans': self.__vlans,
312 'bonds': self.__bonds,
314 'networks': self.__networks}, f)
315 pickle.dump(extras, f)
318 def get_pif_by_uuid(self, uuid):
319 pifs = map(lambda (ref,rec): ref,
320 filter(lambda (ref,rec): uuid == rec['uuid'],
321 self.__pifs.items()))
323 raise Error("Unknown PIF \"%s\"" % uuid)
325 raise Error("Non-unique PIF \"%s\"" % uuid)
329 def get_pifs_by_record(self, record):
330 """record is partial pif record.
331 Get the pif(s) whose record matches.
335 if record[key] != pifrec[key]:
339 return map(lambda (ref,rec): ref,
340 filter(lambda (ref,rec): match(rec),
341 self.__pifs.items()))
343 def get_pif_by_record(self, record):
344 """record is partial pif record.
345 Get the pif whose record matches.
347 pifs = self.get_pifs_by_record(record)
349 raise Error("No matching PIF \"%s\"" % str(record))
351 raise Error("Multiple matching PIFs \"%s\"" % str(record))
355 def get_pif_by_bridge(self, host, bridge):
356 networks = map(lambda (ref,rec): ref,
357 filter(lambda (ref,rec): rec['bridge'] == bridge,
358 self.__networks.items()))
359 if len(networks) == 0:
360 raise Error("No matching network \"%s\"")
363 for network in networks:
364 nwrec = self.get_network_record(network)
365 for pif in nwrec['PIFs']:
366 pifrec = self.get_pif_record(pif)
367 if pifrec['host'] != host:
370 raise Error("Multiple PIFs on %s for network %s" % (host, bridge))
373 raise Error("No PIF on %s for network %s" % (host, bridge))
376 def get_pif_record(self, pif):
377 if self.__pifs.has_key(pif):
378 return self.__pifs[pif]
379 raise Error("Unknown PIF \"%s\"" % pif)
380 def get_all_pifs(self):
382 def pif_exists(self, pif):
383 return self.__pifs.has_key(pif)
385 def get_management_pif(self, host):
386 """ Returns the management pif on host
388 all = self.get_all_pifs()
390 pifrec = self.get_pif_record(pif)
391 if pifrec['management'] and pifrec['host'] == host :
395 def get_network_record(self, network):
396 if self.__networks.has_key(network):
397 return self.__networks[network]
398 raise Error("Unknown network \"%s\"" % network)
399 def get_all_networks(self):
400 return self.__networks
402 def get_bond_record(self, bond):
403 if self.__bonds.has_key(bond):
404 return self.__bonds[bond]
408 def get_vlan_record(self, vlan):
409 if self.__vlans.has_key(vlan):
410 return self.__vlans[vlan]
414 def bridge_name(pif):
415 """Return the bridge name associated with pif, or None if network is bridgeless"""
416 pifrec = db.get_pif_record(pif)
417 nwrec = db.get_network_record(pifrec['network'])
420 # TODO: sanity check that nwrec['bridgeless'] != 'true'
421 return nwrec['bridge']
423 # TODO: sanity check that nwrec['bridgeless'] == 'true'
426 def interface_name(pif):
427 """Construct an interface name from the given PIF record."""
429 pifrec = db.get_pif_record(pif)
431 if pifrec['VLAN'] == '-1':
432 return pifrec['device']
434 return "%(device)s.%(VLAN)s" % pifrec
436 def datapath_name(pif):
437 """Return the OpenFlow datapath name associated with pif.
438 For a non-VLAN PIF, the datapath name is the bridge name.
439 For a VLAN PIF, the datapath name is the bridge name for the PIF's VLAN slave.
440 (xapi will create a datapath named with the bridge name even though we won't
444 pifrec = db.get_pif_record(pif)
446 if pifrec['VLAN'] == '-1':
447 return bridge_name(pif)
449 return bridge_name(get_vlan_slave_of_pif(pif))
452 """Return the the name of the network device that carries the
453 IP configuration (if any) associated with pif.
454 The ipdev name is the same as the bridge name.
457 pifrec = db.get_pif_record(pif)
458 return bridge_name(pif)
460 def get_physdev_pifs(pif):
461 """Return the PIFs for the physical network device(s) associated with pif.
462 For a VLAN PIF, this is the VLAN slave's physical device PIF.
463 For a bond master PIF, these are the bond slave PIFs.
464 For a non-VLAN, non-bond master PIF, the PIF is its own physical device PIF.
467 pifrec = db.get_pif_record(pif)
469 if pifrec['VLAN'] != '-1':
470 return get_physdev_pifs(get_vlan_slave_of_pif(pif))
471 elif len(pifrec['bond_master_of']) != 0:
472 return get_bond_slaves_of_pif(pif)
476 def get_physdev_names(pif):
477 """Return the name(s) of the physical network device(s) associated with pif.
478 For a VLAN PIF, the physical devices are the VLAN slave's physical devices.
479 For a bond master PIF, the physical devices are the bond slaves.
480 For a non-VLAN, non-bond master PIF, the physical device is the PIF itself.
483 return [db.get_pif_record(phys)['device'] for phys in get_physdev_pifs(pif)]
485 def log_pif_action(action, pif):
486 pifrec = db.get_pif_record(pif)
487 pifrec['action'] = action
488 pifrec['interface-name'] = interface_name(pif)
489 if action == "rewrite":
490 pifrec['message'] = "Rewrite PIF %(uuid)s configuration" % pifrec
492 pifrec['message'] = "Bring %(action)s PIF %(uuid)s" % pifrec
493 log("%(message)s: %(interface-name)s configured as %(ip_configuration_mode)s" % pifrec)
495 def get_bond_masters_of_pif(pif):
496 """Returns a list of PIFs which are bond masters of this PIF"""
498 pifrec = db.get_pif_record(pif)
500 bso = pifrec['bond_slave_of']
502 # bond-slave-of is currently a single reference but in principle a
503 # PIF could be a member of several bonds which are not
504 # concurrently attached. Be robust to this possibility.
505 if not bso or bso == "OpaqueRef:NULL":
507 elif not type(bso) == list:
510 bondrecs = [db.get_bond_record(bond) for bond in bso]
511 bondrecs = [rec for rec in bondrecs if rec]
513 return [bond['master'] for bond in bondrecs]
515 def get_bond_slaves_of_pif(pif):
516 """Returns a list of PIFs which make up the given bonded pif."""
518 pifrec = db.get_pif_record(pif)
519 host = pifrec['host']
521 bmo = pifrec['bond_master_of']
523 raise Error("Bond-master-of contains too many elements")
528 bondrec = db.get_bond_record(bmo[0])
530 raise Error("No bond record for bond master PIF")
532 return bondrec['slaves']
534 def get_vlan_slave_of_pif(pif):
535 """Find the PIF which is the VLAN slave of pif.
537 Returns the 'physical' PIF underneath the a VLAN PIF @pif."""
539 pifrec = db.get_pif_record(pif)
541 vlan = pifrec['VLAN_master_of']
542 if not vlan or vlan == "OpaqueRef:NULL":
543 raise Error("PIF is not a VLAN master")
545 vlanrec = db.get_vlan_record(vlan)
547 raise Error("No VLAN record found for PIF")
549 return vlanrec['tagged_PIF']
551 def get_vlan_masters_of_pif(pif):
552 """Returns a list of PIFs which are VLANs on top of the given pif."""
554 pifrec = db.get_pif_record(pif)
555 vlans = [db.get_vlan_record(v) for v in pifrec['VLAN_slave_of']]
556 return [v['untagged_PIF'] for v in vlans if v and db.pif_exists(v['untagged_PIF'])]
558 def interface_deconfigure_commands(interface):
559 # The use of [!0-9] keeps an interface of 'eth0' from matching
560 # VLANs attached to eth0 (such as 'eth0.123'), which are distinct
562 return ['--del-match=bridge.*.port=%s' % interface,
563 '--del-match=bonding.%s.[!0-9]*' % interface,
564 '--del-match=bonding.*.slave=%s' % interface,
565 '--del-match=vlan.%s.[!0-9]*' % interface,
566 '--del-match=port.%s.[!0-9]*' % interface,
567 '--del-match=iface.%s.[!0-9]*' % interface]
569 def run_command(command):
570 log("Running command: " + ' '.join(command))
571 if os.spawnl(os.P_WAIT, command[0], *command) != 0:
572 log("Command failed: " + ' '.join(command))
576 def rename_netdev(old_name, new_name):
577 log("Changing the name of %s to %s" % (old_name, new_name))
578 run_command(['/sbin/ifconfig', old_name, 'down'])
579 if not run_command(['/sbin/ip', 'link', 'set', old_name,
581 raise Error("Could not rename %s to %s" % (old_name, new_name))
583 # Check whether 'pif' exists and has the correct MAC.
584 # If not, try to find a device with the correct MAC and rename it.
585 # 'already_renamed' is used to avoid infinite recursion.
586 def remap_pif(pif, already_renamed=[]):
587 pifrec = db.get_pif_record(pif)
588 device = pifrec['device']
591 # Is there a network device named 'device' at all?
592 device_exists = interface_exists(device)
594 # Yes. Does it have MAC 'mac'?
595 found_mac = get_netdev_mac(device)
596 if found_mac and mac.lower() == found_mac.lower():
597 # Yes, everything checks out the way we want. Nothing to do.
600 log("No network device %s" % device)
602 # What device has MAC 'mac'?
603 cur_device = get_netdev_by_mac(mac)
605 log("No network device has MAC %s" % mac)
608 # First rename 'device', if it exists, to get it out of the way
609 # for 'cur_device' to replace it.
611 rename_netdev(device, "dev%d" % random.getrandbits(24))
613 # Rename 'cur_device' to 'device'.
614 rename_netdev(cur_device, device)
616 def read_first_line_of_file(name):
619 file = open(name, 'r')
620 return file.readline().rstrip('\n')
625 def down_netdev(interface, deconfigure=True):
626 if not interface_exists(interface):
627 log("down_netdev: interface %s does not exist, ignoring" % interface)
631 pidfile_name = '/var/run/dhclient-%s.pid' % interface
633 os.kill(int(read_first_line_of_file(pidfile_name)), signal.SIGTERM)
637 # Remove dhclient pidfile.
639 os.remove(pidfile_name)
643 run_command(["/sbin/ifconfig", interface, '0.0.0.0'])
645 run_command(["/sbin/ifconfig", interface, 'down'])
647 def up_netdev(interface):
648 run_command(["/sbin/ifconfig", interface, 'up'])
650 def find_distinguished_pifs(pif):
651 """Returns the PIFs on host that own DNS and the default route.
652 The peerdns pif will be the one with pif::other-config:peerdns=true, or the mgmt pif if none have this set.
653 The gateway pif will be the one with pif::other-config:defaultroute=true, or the mgmt pif if none have this set.
655 Note: we prune out the bond master pif (if it exists).
656 This is because when we are called to bring up an interface with a bond master, it is implicit that
657 we should bring down that master."""
659 pifrec = db.get_pif_record(pif)
660 host = pifrec['host']
662 pifs_on_host = [ __pif for __pif in db.get_all_pifs() if
663 db.get_pif_record(__pif)['host'] == host and
664 (not __pif in get_bond_masters_of_pif(pif)) ]
667 defaultroute_pif = None
669 # loop through all the pifs on this host looking for one with
670 # other-config:peerdns = true, and one with
671 # other-config:default-route=true
672 for __pif in pifs_on_host:
673 __pifrec = db.get_pif_record(__pif)
674 __oc = __pifrec['other_config']
675 if __oc.has_key('peerdns') and __oc['peerdns'] == 'true':
676 if peerdns_pif == None:
679 log('Warning: multiple pifs with "peerdns=true" - choosing %s and ignoring %s' % \
680 (db.get_pif_record(peerdns_pif)['device'], __pifrec['device']))
681 if __oc.has_key('defaultroute') and __oc['defaultroute'] == 'true':
682 if defaultroute_pif == None:
683 defaultroute_pif = __pif
685 log('Warning: multiple pifs with "defaultroute=true" - choosing %s and ignoring %s' % \
686 (db.get_pif_record(defaultroute_pif)['device'], __pifrec['device']))
688 # If no pif is explicitly specified then use the mgmt pif for peerdns/defaultroute
689 if peerdns_pif == None:
690 peerdns_pif = management_pif
691 if defaultroute_pif == None:
692 defaultroute_pif = management_pif
694 return peerdns_pif, defaultroute_pif
696 def run_ethtool(device, oc):
697 # Run "ethtool -s" if there are any settings.
699 if oc.has_key('ethtool-speed'):
700 val = oc['ethtool-speed']
701 if val in ["10", "100", "1000"]:
702 settings += ['speed', val]
704 log("Invalid value for ethtool-speed = %s. Must be 10|100|1000." % val)
705 if oc.has_key('ethtool-duplex'):
706 val = oc['ethtool-duplex']
707 if val in ["10", "100", "1000"]:
708 settings += ['duplex', 'val']
710 log("Invalid value for ethtool-duplex = %s. Must be half|full." % val)
711 if oc.has_key('ethtool-autoneg'):
712 val = oc['ethtool-autoneg']
713 if val in ["true", "on"]:
714 settings += ['autoneg', 'on']
715 elif val in ["false", "off"]:
716 settings += ['autoneg', 'off']
718 log("Invalid value for ethtool-autoneg = %s. Must be on|true|off|false." % val)
720 run_command(['/sbin/ethtool', '-s', device] + settings)
722 # Run "ethtool -K" if there are any offload settings.
724 for opt in ("rx", "tx", "sg", "tso", "ufo", "gso"):
725 if oc.has_key("ethtool-" + opt):
726 val = oc["ethtool-" + opt]
727 if val in ["true", "on"]:
728 offload += [opt, 'on']
729 elif val in ["false", "off"]:
730 offload += [opt, 'off']
732 log("Invalid value for ethtool-%s = %s. Must be on|true|off|false." % (opt, val))
734 run_command(['/sbin/ethtool', '-K', device] + offload)
737 if oc.has_key('mtu'):
739 int(oc['mtu']) # Check that the value is an integer
740 return ['mtu', oc['mtu']]
741 except ValueError, x:
742 log("Invalid value for mtu = %s" % mtu)
745 def configure_local_port(pif):
746 pifrec = db.get_pif_record(pif)
747 datapath = datapath_name(pif)
748 ipdev = ipdev_name(pif)
750 host = pifrec['host']
751 nw = pifrec['network']
752 nwrec = db.get_network_record(nw)
754 pif_oc = pifrec['other_config']
755 nw_oc = nwrec['other_config']
757 # IP (except DHCP) and MTU.
758 ifconfig_argv = ['/sbin/ifconfig', ipdev, 'up']
760 if pifrec['ip_configuration_mode'] == "DHCP":
762 elif pifrec['ip_configuration_mode'] == "Static":
763 ifconfig_argv += [pifrec['IP']]
764 ifconfig_argv += ['netmask', pifrec['netmask']]
765 gateway = pifrec['gateway']
766 elif pifrec['ip_configuration_mode'] == "None":
770 raise Error("Unknown IP-configuration-mode %s" % pifrec['ip_configuration_mode'])
771 ifconfig_argv += mtu_setting(nw_oc)
772 run_command(ifconfig_argv)
774 (peerdns_pif, defaultroute_pif) = find_distinguished_pifs(pif)
777 if peerdns_pif == pif:
778 f = ConfigurationFile('resolv.conf', "/etc")
779 if pif_oc.has_key('domain'):
780 f.write("search %s\n" % pif_oc['domain'])
781 for dns in pifrec['DNS'].split(","):
782 f.write("nameserver %s\n" % dns)
788 if defaultroute_pif == pif and gateway != '':
789 run_command(['/sbin/ip', 'route', 'replace', 'default',
790 'via', gateway, 'dev', ipdev])
791 if nw_oc.has_key('static-routes'):
792 for line in nw_oc['static-routes'].split(','):
793 network, masklen, gateway = line.split('/')
794 run_command(['/sbin/ip', 'route', 'add',
795 '%s/%s' % (network, masklen), 'via', gateway,
799 run_ethtool(ipdev, nw_oc)
802 if pifrec['ip_configuration_mode'] == "DHCP":
804 print "Determining IP information for %s..." % ipdev,
805 argv = ['/sbin/dhclient', '-q',
806 '-lf', '/var/lib/dhclient/dhclient-%s.leases' % ipdev,
807 '-pf', '/var/run/dhclient-%s.pid' % ipdev,
809 if run_command(argv):
814 def configure_physdev(pif):
815 pifrec = db.get_pif_record(pif)
816 device = pifrec['device']
817 oc = pifrec['other_config']
819 run_command(['/sbin/ifconfig', device, 'up'] + mtu_setting(oc))
820 run_ethtool(device, oc)
822 def modify_config(commands):
823 run_command(['/root/vswitch/bin/ovs-cfg-mod', '-vANY:console:emer',
824 '-F', '/etc/ovs-vswitchd.conf']
826 run_command(['/sbin/service', 'vswitch', 'reload'])
828 def is_bond_pif(pif):
829 pifrec = db.get_pif_record(pif)
830 return len(pifrec['bond_master_of']) != 0
832 def configure_bond(pif):
833 pifrec = db.get_pif_record(pif)
834 interface = interface_name(pif)
835 ipdev = ipdev_name(pif)
836 datapath = datapath_name(pif)
837 physdev_names = get_physdev_names(pif)
839 argv = ['--del-match=bonding.%s.[!0-9]*' % interface]
840 argv += ["--add=bonding.%s.slave=%s" % (interface, slave)
841 for slave in physdev_names]
842 argv += ['--add=bonding.%s.fake-iface=true' % interface]
844 if pifrec['MAC'] != "":
845 argv += ['--add=port.%s.mac=%s' % (interface, pifrec['MAC'])]
849 "mode": "balance-slb",
855 # override defaults with values from other-config whose keys
857 oc = pifrec['other_config']
858 overrides = filter(lambda (key,val):
859 key.startswith("bond-"), oc.items())
860 overrides = map(lambda (key,val): (key[5:], val), overrides)
861 bond_options.update(overrides)
862 for (name,val) in bond_options.items():
863 argv += ["--add=bonding.%s.%s=%s" % (interface, name, val)]
867 pifrec = db.get_pif_record(pif)
869 bridge = bridge_name(pif)
870 interface = interface_name(pif)
871 ipdev = ipdev_name(pif)
872 datapath = datapath_name(pif)
873 physdev_pifs = get_physdev_pifs(pif)
874 physdev_names = get_physdev_names(pif)
876 if pifrec['VLAN'] != '-1':
877 vlan_slave = get_vlan_slave_of_pif(pif)
878 if vlan_slave and is_bond_pif(vlan_slave):
879 bond_master = vlan_slave
880 elif is_bond_pif(pif):
885 bond_slaves = get_bond_slaves_of_pif(bond_master)
888 bond_masters = get_bond_masters_of_pif(pif)
890 # Support "rpm -e vswitch" gracefully by keeping Centos configuration
891 # files up-to-date, even though we don't use them or need them.
892 f = configure_pif(pif)
893 mode = pifrec['ip_configuration_mode']
895 log("Configuring %s using %s configuration" % (bridge, mode))
896 br = open_network_ifcfg(pif)
897 configure_network(pif, br)
901 log("Configuring %s using %s configuration" % (interface, mode))
902 configure_network(pif, f)
904 for master in bond_masters:
905 master_bridge = bridge_name(master)
906 removed = unconfigure_pif(master)
907 f.attach_child(removed)
909 removed = open_network_ifcfg(master)
910 log("Unlinking stale file %s" % removed.path())
912 f.attach_child(removed)
914 # /etc/xensource/scripts/vif needs to know where to add VIFs.
916 if not os.path.exists(vswitch_config_dir):
917 os.mkdir(vswitch_config_dir)
918 br = ConfigurationFile("br-%s" % bridge, vswitch_config_dir)
919 br.write("VLAN_SLAVE=%s\n" % datapath)
920 br.write("VLAN_VID=%s\n" % pifrec['VLAN'])
924 # Update all configuration files (both ours and Centos's).
928 # Check the MAC address of each network device and remap if
929 # necessary to make names match our expectations.
930 for physdev_pif in physdev_pifs:
931 remap_pif(physdev_pif)
933 # "ifconfig down" the network device and delete its IP address, etc.
935 for physdev_name in physdev_names:
936 down_netdev(physdev_name)
938 # If we are bringing up a bond, remove IP addresses from the
939 # slaves (because we are implicitly being asked to take them down).
941 # Conversely, if we are bringing up an interface that has bond
942 # masters, remove IP addresses from the bond master (because we
943 # are implicitly being asked to take it down).
944 for bond_pif in bond_slaves + bond_masters:
945 run_command(["/sbin/ifconfig", ipdev_name(bond_pif), '0.0.0.0'])
947 # Remove all keys related to pif and any bond masters linked to PIF.
948 del_ports = [ipdev] + physdev_names + bond_masters
949 if vlan_slave and bond_master:
950 del_ports += [interface_name(bond_master)]
952 # What ports do we need to add to the datapath?
954 # We definitely need the ipdev, and ordinarily we want the
955 # physical devices too, but for bonds we need the bond as bridge
957 add_ports = [ipdev, datapath]
959 add_ports += physdev_names
961 add_ports += [interface_name(bond_master)]
963 # What ports do we need to delete?
965 # - All the ports that we add, to avoid duplication and to drop
966 # them from another datapath in case they're misassigned.
968 # - The physical devices, since they will either be in add_ports
969 # or added to the bonding device (see below).
971 # - The bond masters for pif. (Ordinarily pif shouldn't have any
972 # bond masters. If it does then interface-reconfigure is
973 # implicitly being asked to take them down.)
974 del_ports = add_ports + physdev_names + bond_masters
976 # What networks does this datapath carry?
978 # - The network corresponding to the datapath's PIF.
980 # - The networks corresponding to any VLANs attached to the
983 for nwpif in db.get_pifs_by_record({'device': pifrec['device'],
984 'host': pifrec['host']}):
985 net = db.get_pif_record(nwpif)['network']
986 network_uuids += [db.get_network_record(net)['uuid']]
988 # Bring up bond slaves early, because ovs-vswitchd initially
989 # enables or disables bond slaves based on whether carrier is
990 # detected when they are added, and a network device that is down
991 # always reports "no carrier".
992 bond_slave_physdev_pifs = []
993 for slave in bond_slaves:
994 bond_slave_physdev_pifs += get_physdev_pifs(slave)
995 for slave_physdev_pif in set(bond_slave_physdev_pifs):
996 configure_physdev(slave_physdev_pif)
998 # Now modify the ovs-vswitchd config file.
1000 for port in set(del_ports):
1001 argv += interface_deconfigure_commands(port)
1002 for port in set(add_ports):
1003 argv += ['--add=bridge.%s.port=%s' % (datapath, port)]
1005 argv += ['--add=vlan.%s.tag=%s' % (ipdev, pifrec['VLAN'])]
1006 argv += ['--add=iface.%s.internal=true' % (ipdev)]
1008 # xapi creates a bridge by the name of the ipdev and requires
1009 # that the IP address will be on it. We need to delete this
1010 # bridge because we need that device to be a member of our
1012 argv += ['--del-match=bridge.%s.[!0-9]*' % ipdev]
1014 # xapi insists that its attempts to create the bridge succeed,
1015 # so force that to happen.
1016 argv += ['--add=iface.%s.fake-bridge=true' % (ipdev)]
1019 os.unlink("%s/br-%s" % (vswitch_config_dir, bridge))
1022 argv += ['--del-match=bridge.%s.xs-network-uuids=*' % datapath]
1023 argv += ['--add=bridge.%s.xs-network-uuids=%s' % (datapath, uuid)
1024 for uuid in set(network_uuids)]
1026 argv += configure_bond(bond_master)
1029 # Bring up VLAN slave, plus physical devices other than bond
1030 # slaves (which we brought up earlier).
1032 up_netdev(ipdev_name(vlan_slave))
1033 for physdev_pif in set(physdev_pifs) - set(bond_slave_physdev_pifs):
1034 configure_physdev(physdev_pif)
1036 # Configure network device for local port.
1037 configure_local_port(pif)
1039 # Update /etc/issue (which contains the IP address of the management interface)
1040 os.system("/sbin/update-issue")
1043 # There seems to be a race somewhere: without this sleep, using
1044 # XenCenter to create a bond that becomes the management interface
1045 # fails with "The underlying connection was closed: A connection that
1046 # was expected to be kept alive was closed by the server." on every
1047 # second or third try, even though /var/log/messages doesn't show
1050 # The race is probably present even without vswitch, but bringing up a
1051 # bond without vswitch involves a built-in pause of 10 seconds or more
1052 # to wait for the bond to transition from learning to forwarding state.
1055 def action_down(pif):
1056 rec = db.get_pif_record(pif)
1057 interface = interface_name(pif)
1058 bridge = bridge_name(pif)
1059 ipdev = ipdev_name(pif)
1061 # Support "rpm -e vswitch" gracefully by keeping Centos configuration
1062 # files up-to-date, even though we don't use them or need them.
1063 f = unconfigure_pif(pif)
1065 br = open_network_ifcfg(pif)
1066 log("Unlinking stale file %s" % br.path())
1073 log("action_down failed to apply changes: %s" % e.msg)
1078 if rec['VLAN'] != '-1':
1079 # Get rid of the VLAN device itself.
1081 argv += interface_deconfigure_commands(ipdev)
1083 # If the VLAN's slave is attached, stop here.
1084 slave = get_vlan_slave_of_pif(pif)
1085 if db.get_pif_record(slave)['currently_attached']:
1086 log("VLAN slave is currently attached")
1090 # If the VLAN's slave has other VLANs that are attached, stop here.
1091 masters = get_vlan_masters_of_pif(slave)
1093 if m != pif and db.get_pif_record(m)['currently_attached']:
1094 log("VLAN slave has other master %s" % interface_naem(m))
1098 # Otherwise, take down the VLAN's slave too.
1099 log("No more masters, bring down vlan slave %s" % interface_name(slave))
1102 # Stop here if this PIF has attached VLAN masters.
1103 vlan_masters = get_vlan_masters_of_pif(pif)
1104 log("VLAN masters of %s - %s" % (rec['device'], [interface_name(m) for m in vlan_masters]))
1105 for m in vlan_masters:
1106 if db.get_pif_record(m)['currently_attached']:
1107 log("Leaving %s up due to currently attached VLAN master %s" % (interface, interface_name(m)))
1110 # pif is now either a bond or a physical device which needs to be
1111 # brought down. pif might have changed so re-check all its attributes.
1112 rec = db.get_pif_record(pif)
1113 interface = interface_name(pif)
1114 bridge = bridge_name(pif)
1115 ipdev = ipdev_name(pif)
1118 bond_slaves = get_bond_slaves_of_pif(pif)
1119 log("bond slaves of %s - %s" % (rec['device'], [interface_name(s) for s in bond_slaves]))
1120 for slave in bond_slaves:
1121 slave_interface = interface_name(slave)
1122 log("bring down bond slave %s" % slave_interface)
1123 argv += interface_deconfigure_commands(slave_interface)
1124 down_netdev(slave_interface)
1126 argv += interface_deconfigure_commands(ipdev)
1129 argv += ['--del-match', 'bridge.%s.*' % datapath_name(pif)]
1130 argv += ['--del-match', 'bonding.%s.[!0-9]*' % interface]
1133 def action_rewrite(pif):
1134 # Support "rpm -e vswitch" gracefully by keeping Centos configuration
1135 # files up-to-date, even though we don't use them or need them.
1136 pifrec = db.get_pif_record(pif)
1137 f = configure_pif(pif)
1138 interface = interface_name(pif)
1139 bridge = bridge_name(pif)
1140 mode = pifrec['ip_configuration_mode']
1142 log("Configuring %s using %s configuration" % (bridge, mode))
1143 br = open_network_ifcfg(pif)
1144 configure_network(pif, br)
1148 log("Configuring %s using %s configuration" % (interface, mode))
1149 configure_network(pif, f)
1155 log("failed to apply changes: %s" % e.msg)
1159 # We have no code of our own to run here.
1162 def main(argv=None):
1163 global output_directory, management_pif
1169 force_interface = None
1170 force_management = False
1178 longops = [ "output-directory=",
1179 "pif=", "pif-uuid=",
1185 "device=", "mode=", "ip=", "netmask=", "gateway=",
1187 arglist, args = getopt.gnu_getopt(argv[1:], shortops, longops)
1188 except getopt.GetoptError, msg:
1191 force_rewrite_config = {}
1194 if o == "--output-directory":
1195 output_directory = a
1198 elif o == "--pif-uuid":
1200 elif o == "--session":
1202 elif o == "--force-interface" or o == "--force":
1204 elif o == "--management":
1205 force_management = True
1206 elif o in ["--device", "--mode", "--ip", "--netmask", "--gateway"]:
1207 force_rewrite_config[o[2:]] = a
1208 elif o == "-h" or o == "--help":
1209 print __doc__ % {'command-name': os.path.basename(argv[0])}
1212 if not debug_mode():
1213 syslog.openlog(os.path.basename(argv[0]))
1214 log("Called as " + str.join(" ", argv))
1216 raise Usage("Required option <action> not present")
1218 raise Usage("Too many arguments")
1221 # backwards compatibility
1222 if action == "rewrite-configuration": action = "rewrite"
1224 if output_directory and ( session or pif ):
1225 raise Usage("--session/--pif cannot be used with --output-directory")
1226 if ( session or pif ) and pif_uuid:
1227 raise Usage("--session/--pif and --pif-uuid are mutually exclusive.")
1228 if ( session and not pif ) or ( not session and pif ):
1229 raise Usage("--session and --pif must be used together.")
1230 if force_interface and ( session or pif or pif_uuid ):
1231 raise Usage("--force is mutually exclusive with --session, --pif and --pif-uuid")
1232 if len(force_rewrite_config) and not (force_interface and action == "rewrite"):
1233 raise Usage("\"--force rewrite\" needed for --device, --mode, --ip, --netmask, and --gateway")
1237 log("Force interface %s %s" % (force_interface, action))
1239 if action == "rewrite":
1240 action_force_rewrite(force_interface, force_rewrite_config)
1242 db = DatabaseCache(cache_file=dbcache_file)
1243 host = db.extras['host']
1244 pif = db.get_pif_by_bridge(host, force_interface)
1245 management_pif = db.get_management_pif(host)
1249 elif action == "down":
1252 raise Usage("Unknown action %s" % action)
1254 db = DatabaseCache(session_ref=session)
1257 pif = db.get_pif_by_uuid(pif_uuid)
1260 raise Usage("No PIF given")
1262 if force_management:
1263 # pif is going to be the management pif
1264 management_pif = pif
1266 # pif is not going to be the management pif.
1267 # Search DB cache for pif on same host with management=true
1268 pifrec = db.get_pif_record(pif)
1269 host = pifrec['host']
1270 management_pif = db.get_management_pif(host)
1272 log_pif_action(action, pif)
1274 if not check_allowed(pif):
1279 elif action == "down":
1281 elif action == "rewrite":
1284 raise Usage("Unknown action %s" % action)
1287 pifrec = db.get_pif_record(pif)
1288 db.save(dbcache_file, {'host': pifrec['host']})
1291 print >>sys.stderr, err.msg
1292 print >>sys.stderr, "For help use --help."
1300 # The following code allows interface-reconfigure to keep Centos
1301 # network configuration files up-to-date, even though the vswitch
1302 # never uses them. In turn, that means that "rpm -e vswitch" does not
1303 # have to update any configuration files.
1305 def configure_ethtool(oc, f):
1306 # Options for "ethtool -s"
1308 setting_opts = ["autoneg", "speed", "duplex"]
1309 # Options for "ethtool -K"
1311 offload_opts = ["rx", "tx", "sg", "tso", "ufo", "gso"]
1313 for opt in [opt for opt in setting_opts + offload_opts if oc.has_key("ethtool-" + opt)]:
1314 val = oc["ethtool-" + opt]
1316 if opt in ["speed"]:
1317 if val in ["10", "100", "1000"]:
1318 val = "speed " + val
1320 log("Invalid value for ethtool-speed = %s. Must be 10|100|1000." % val)
1322 elif opt in ["duplex"]:
1323 if val in ["half", "full"]:
1324 val = "duplex " + val
1326 log("Invalid value for ethtool-duplex = %s. Must be half|full." % val)
1328 elif opt in ["autoneg"] + offload_opts:
1329 if val in ["true", "on"]:
1331 elif val in ["false", "off"]:
1334 log("Invalid value for ethtool-%s = %s. Must be on|true|off|false." % (opt, val))
1337 if opt in setting_opts:
1338 if val and settings:
1339 settings = settings + " " + val
1342 elif opt in offload_opts:
1344 offload = offload + " " + val
1349 f.write("ETHTOOL_OPTS=\"%s\"\n" % settings)
1351 f.write("ETHTOOL_OFFLOAD_OPTS=\"%s\"\n" % offload)
1353 def configure_mtu(oc, f):
1354 if not oc.has_key('mtu'):
1358 mtu = int(oc['mtu'])
1359 f.write("MTU=%d\n" % mtu)
1360 except ValueError, x:
1361 log("Invalid value for mtu = %s" % mtu)
1363 def configure_static_routes(interface, oc, f):
1364 """Open a route-<interface> file for static routes.
1366 Opens the static routes configuration file for interface and writes one
1367 line for each route specified in the network's other config "static-routes" value.
1369 interface ( RO): xenbr1
1370 other-config (MRW): static-routes: 172.16.0.0/15/192.168.0.3,172.18.0.0/16/192.168.0.4;...
1372 Then route-xenbr1 should be
1373 172.16.0.0/15 via 192.168.0.3 dev xenbr1
1374 172.18.0.0/16 via 192.168.0.4 dev xenbr1
1376 fname = "route-%s" % interface
1377 if oc.has_key('static-routes'):
1378 # The key is present - extract comma seperates entries
1379 lines = oc['static-routes'].split(',')
1381 # The key is not present, i.e. there are no static routes
1384 child = ConfigurationFile(fname)
1385 child.write("# DO NOT EDIT: This file (%s) was autogenerated by %s\n" % \
1386 (os.path.basename(child.path()), os.path.basename(sys.argv[0])))
1390 network, masklen, gateway = l.split('/')
1391 child.write("%s/%s via %s dev %s\n" % (network, masklen, gateway, interface))
1393 f.attach_child(child)
1396 except ValueError, e:
1397 log("Error in other-config['static-routes'] format for network %s: %s" % (interface, e))
1399 def __open_ifcfg(interface):
1400 """Open a network interface configuration file.
1402 Opens the configuration file for interface, writes a header and
1403 common options and returns the file object.
1405 fname = "ifcfg-%s" % interface
1406 f = ConfigurationFile(fname)
1408 f.write("# DO NOT EDIT: This file (%s) was autogenerated by %s\n" % \
1409 (os.path.basename(f.path()), os.path.basename(sys.argv[0])))
1410 f.write("XEMANAGED=yes\n")
1411 f.write("DEVICE=%s\n" % interface)
1412 f.write("ONBOOT=no\n")
1416 def open_network_ifcfg(pif):
1417 bridge = bridge_name(pif)
1418 interface = interface_name(pif)
1420 return __open_ifcfg(bridge)
1422 return __open_ifcfg(interface)
1425 def open_pif_ifcfg(pif):
1426 pifrec = db.get_pif_record(pif)
1428 log("Configuring %s (%s)" % (interface_name(pif), pifrec['MAC']))
1430 f = __open_ifcfg(interface_name(pif))
1432 if pifrec.has_key('other_config'):
1433 configure_ethtool(pifrec['other_config'], f)
1434 configure_mtu(pifrec['other_config'], f)
1438 def configure_network(pif, f):
1439 """Write the configuration file for a network.
1441 Writes configuration derived from the network object into the relevant
1442 ifcfg file. The configuration file is passed in, but if the network is
1443 bridgeless it will be ifcfg-<interface>, otherwise it will be ifcfg-<bridge>.
1445 This routine may also write ifcfg files of the networks corresponding to other PIFs
1446 in order to maintain consistency.
1449 pif: Opaque_ref of pif
1450 f : ConfigurationFile(/path/to/ifcfg) to which we append network configuration
1453 pifrec = db.get_pif_record(pif)
1454 host = pifrec['host']
1455 nw = pifrec['network']
1456 nwrec = db.get_network_record(nw)
1458 bridge = bridge_name(pif)
1459 interface = interface_name(pif)
1465 if nwrec.has_key('other_config'):
1466 configure_ethtool(nwrec['other_config'], f)
1467 configure_mtu(nwrec['other_config'], f)
1468 configure_static_routes(device, nwrec['other_config'], f)
1471 if pifrec.has_key('other_config'):
1472 oc = pifrec['other_config']
1474 if device == bridge:
1475 f.write("TYPE=Bridge\n")
1476 f.write("DELAY=0\n")
1477 f.write("STP=off\n")
1478 f.write("PIFDEV=%s\n" % interface_name(pif))
1480 if pifrec['ip_configuration_mode'] == "DHCP":
1481 f.write("BOOTPROTO=dhcp\n")
1482 f.write("PERSISTENT_DHCLIENT=yes\n")
1483 elif pifrec['ip_configuration_mode'] == "Static":
1484 f.write("BOOTPROTO=none\n")
1485 f.write("NETMASK=%(netmask)s\n" % pifrec)
1486 f.write("IPADDR=%(IP)s\n" % pifrec)
1487 f.write("GATEWAY=%(gateway)s\n" % pifrec)
1488 elif pifrec['ip_configuration_mode'] == "None":
1489 f.write("BOOTPROTO=none\n")
1491 raise Error("Unknown ip-configuration-mode %s" % pifrec['ip_configuration_mode'])
1493 if pifrec.has_key('DNS') and pifrec['DNS'] != "":
1494 ServerList = pifrec['DNS'].split(",")
1495 for i in range(len(ServerList)): f.write("DNS%d=%s\n" % (i+1, ServerList[i]))
1496 if oc and oc.has_key('domain'):
1497 f.write("DOMAIN='%s'\n" % oc['domain'].replace(',', ' '))
1499 # We only allow one ifcfg-xenbr* to have PEERDNS=yes and there can be only one GATEWAYDEV in /etc/sysconfig/network.
1500 # The peerdns pif will be the one with pif::other-config:peerdns=true, or the mgmt pif if none have this set.
1501 # The gateway pif will be the one with pif::other-config:defaultroute=true, or the mgmt pif if none have this set.
1503 # Work out which pif on this host should be the one with PEERDNS=yes and which should be the GATEWAYDEV
1505 # Note: we prune out the bond master pif (if it exists).
1506 # This is because when we are called to bring up an interface with a bond master, it is implicit that
1507 # we should bring down that master.
1508 pifs_on_host = [ __pif for __pif in db.get_all_pifs() if
1509 db.get_pif_record(__pif)['host'] == host and
1510 (not __pif in get_bond_masters_of_pif(pif)) ]
1511 other_pifs_on_host = [ __pif for __pif in pifs_on_host if __pif != pif ]
1514 defaultroute_pif = None
1516 # loop through all the pifs on this host looking for one with
1517 # other-config:peerdns = true, and one with
1518 # other-config:default-route=true
1519 for __pif in pifs_on_host:
1520 __pifrec = db.get_pif_record(__pif)
1521 __oc = __pifrec['other_config']
1522 if __oc.has_key('peerdns') and __oc['peerdns'] == 'true':
1523 if peerdns_pif == None:
1526 log('Warning: multiple pifs with "peerdns=true" - choosing %s and ignoring %s' % \
1527 (db.get_pif_record(peerdns_pif)['device'], __pifrec['device']))
1528 if __oc.has_key('defaultroute') and __oc['defaultroute'] == 'true':
1529 if defaultroute_pif == None:
1530 defaultroute_pif = __pif
1532 log('Warning: multiple pifs with "defaultroute=true" - choosing %s and ignoring %s' % \
1533 (db.get_pif_record(defaultroute_pif)['device'], __pifrec['device']))
1535 # If no pif is explicitly specified then use the mgmt pif for peerdns/defaultroute
1536 if peerdns_pif == None:
1537 peerdns_pif = management_pif
1538 if defaultroute_pif == None:
1539 defaultroute_pif = management_pif
1541 # Update all the other network's ifcfg files and ensure consistency
1542 for __pif in other_pifs_on_host:
1543 __f = open_network_ifcfg(__pif)
1544 peerdns_line_wanted = 'PEERDNS=%s\n' % ((__pif == peerdns_pif) and 'yes' or 'no')
1545 lines = __f.readlines()
1547 if not peerdns_line_wanted in lines:
1548 # the PIF selected for DNS has changed and as a result this ifcfg file needs rewriting
1550 if not line.lstrip().startswith('PEERDNS'):
1552 log("Setting %s in %s" % (peerdns_line_wanted.strip(), __f.path()))
1553 __f.write(peerdns_line_wanted)
1558 # There is no need to change this ifcfg file. So don't attach_child.
1561 # ... and for this pif too
1562 f.write('PEERDNS=%s\n' % ((pif == peerdns_pif) and 'yes' or 'no'))
1565 fnetwork = ConfigurationFile("network", "/etc/sysconfig")
1566 for line in fnetwork.readlines():
1567 if line.lstrip().startswith('GATEWAY') :
1569 fnetwork.write(line)
1570 if defaultroute_pif:
1571 gatewaydev = bridge_name(defaultroute_pif)
1573 gatewaydev = interface_name(defaultroute_pif)
1574 fnetwork.write('GATEWAYDEV=%s\n' % gatewaydev)
1576 f.attach_child(fnetwork)
1581 def configure_physical_interface(pif):
1582 """Write the configuration for a physical interface.
1584 Writes the configuration file for the physical interface described by
1587 Returns the open file handle for the interface configuration file.
1590 pifrec = db.get_pif_record(pif)
1592 f = open_pif_ifcfg(pif)
1594 f.write("TYPE=Ethernet\n")
1595 f.write("HWADDR=%(MAC)s\n" % pifrec)
1599 def configure_bond_interface(pif):
1600 """Write the configuration for a bond interface.
1602 Writes the configuration file for the bond interface described by
1603 the pif object. Handles writing the configuration for the slave
1606 Returns the open file handle for the bond interface configuration
1610 pifrec = db.get_pif_record(pif)
1611 oc = pifrec['other_config']
1612 f = open_pif_ifcfg(pif)
1614 if pifrec['MAC'] != "":
1615 f.write("MACADDR=%s\n" % pifrec['MAC'])
1617 for slave in get_bond_slaves_of_pif(pif):
1618 s = configure_physical_interface(slave)
1619 s.write("MASTER=%(device)s\n" % pifrec)
1620 s.write("SLAVE=yes\n")
1624 # The bond option defaults
1626 "mode": "balance-slb",
1633 # override defaults with values from other-config whose keys being with "bond-"
1634 overrides = filter(lambda (key,val): key.startswith("bond-"), oc.items())
1635 overrides = map(lambda (key,val): (key[5:], val), overrides)
1636 bond_options.update(overrides)
1638 # write the bond options to ifcfg-bondX
1639 f.write('BONDING_OPTS="')
1640 for (name,val) in bond_options.items():
1641 f.write("%s=%s " % (name,val))
1645 def configure_vlan_interface(pif):
1646 """Write the configuration for a VLAN interface.
1648 Writes the configuration file for the VLAN interface described by
1649 the pif object. Handles writing the configuration for the master
1650 interface if necessary.
1652 Returns the open file handle for the VLAN interface configuration
1656 slave = configure_pif(get_vlan_slave_of_pif(pif))
1659 f = open_pif_ifcfg(pif)
1660 f.write("VLAN=yes\n")
1661 f.attach_child(slave)
1665 def configure_pif(pif):
1666 """Write the configuration for a PIF object.
1668 Writes the configuration file the PIF and all dependent
1669 interfaces (bond slaves and VLAN masters etc).
1671 Returns the open file handle for the interface configuration file.
1674 pifrec = db.get_pif_record(pif)
1676 if pifrec['VLAN'] != '-1':
1677 f = configure_vlan_interface(pif)
1678 elif len(pifrec['bond_master_of']) != 0:
1679 f = configure_bond_interface(pif)
1681 f = configure_physical_interface(pif)
1683 bridge = bridge_name(pif)
1685 f.write("BRIDGE=%s\n" % bridge)
1689 def unconfigure_pif(pif):
1690 """Clear up the files created by configure_pif"""
1691 f = open_pif_ifcfg(pif)
1692 log("Unlinking stale file %s" % f.path())
1696 if __name__ == "__main__":
1702 err = traceback.format_exception(*ex)
1706 if not debug_mode():