]> git.proxmox.com Git - ovs.git/blame - xenserver/opt_xensource_libexec_interface-reconfigure
netdev-linux: Improve netdev_linux_set_etheraddr().
[ovs.git] / xenserver / opt_xensource_libexec_interface-reconfigure
CommitLineData
064af421
BP
1#!/usr/bin/python
2#
42ab0203 3# Copyright (c) 2008,2009 Citrix Systems, Inc. All rights reserved.
064af421
BP
4# Copyright (c) 2009 Nicira Networks.
5#
6"""Usage:
7
057fed2b
BP
8 %(command-name)s <PIF> up
9 %(command-name)s <PIF> down
10 %(command-name)s [<PIF>] rewrite
11 %(command-name)s --force <BRIDGE> up
12 %(command-name)s --force <BRIDGE> down
13 %(command-name)s --force <BRIDGE> rewrite --device=<INTERFACE> <CONFIG>
064af421
BP
14 %(command-name)s --force all down
15
057fed2b
BP
16 where <PIF> is one of:
17 --session <SESSION-REF> --pif <PIF-REF>
18 --pif-uuid <PIF-UUID>
19 and <CONFIG> is one of:
20 --mode=dhcp
21 --mode=static --ip=<IPADDR> --netmask=<NM> [--gateway=<GW>]
064af421
BP
22
23 Options:
24 --session A session reference to use to access the xapi DB
057fed2b
BP
25 --pif A PIF reference within the session.
26 --pif-uuid The UUID of a PIF.
27 --force An interface name.
064af421
BP
28"""
29
30#
31# Undocumented parameters for test & dev:
32#
33# --output-directory=<DIR> Write configuration to <DIR>. Also disables actually
34# raising/lowering the interfaces
064af421
BP
35#
36#
37#
38# Notes:
39# 1. Every pif belongs to exactly one network
40# 2. Every network has zero or one pifs
41# 3. A network may have an associated bridge, allowing vifs to be attached
42# 4. A network may be bridgeless (there's no point having a bridge over a storage pif)
43
064af421
BP
44import XenAPI
45import os, sys, getopt, time, signal
46import syslog
47import traceback
48import time
49import re
2bb451b6 50import random
6dd3fad4
IC
51from xml.dom.minidom import getDOMImplementation
52from xml.dom.minidom import parse as parseXML
064af421
BP
53
54output_directory = None
55
56db = None
57management_pif = None
58
e44d1844
IC
59vswitch_state_dir = "/var/lib/openvswitch/"
60dbcache_file = vswitch_state_dir + "dbcache"
064af421
BP
61
62class Usage(Exception):
63 def __init__(self, msg):
64 Exception.__init__(self)
65 self.msg = msg
66
67class Error(Exception):
68 def __init__(self, msg):
69 Exception.__init__(self)
70 self.msg = msg
71
72class ConfigurationFile(object):
73 """Write a file, tracking old and new versions.
74
75 Supports writing a new version of a file and applying and
76 reverting those changes.
77 """
78
79 __STATE = {"OPEN":"OPEN",
80 "NOT-APPLIED":"NOT-APPLIED", "APPLIED":"APPLIED",
81 "REVERTED":"REVERTED", "COMMITTED": "COMMITTED"}
82
83 def __init__(self, fname, path="/etc/sysconfig/network-scripts"):
84
85 self.__state = self.__STATE['OPEN']
86 self.__fname = fname
87 self.__children = []
88
89 if debug_mode():
90 dirname = output_directory
91 else:
92 dirname = path
93
94 self.__path = os.path.join(dirname, fname)
95 self.__oldpath = os.path.join(dirname, "." + fname + ".xapi-old")
96 self.__newpath = os.path.join(dirname, "." + fname + ".xapi-new")
97 self.__unlink = False
98
99 self.__f = open(self.__newpath, "w")
100
101 def attach_child(self, child):
102 self.__children.append(child)
103
104 def path(self):
105 return self.__path
106
107 def readlines(self):
108 try:
109 return open(self.path()).readlines()
110 except:
111 return ""
112
113 def write(self, args):
114 if self.__state != self.__STATE['OPEN']:
115 raise Error("Attempt to write to file in state %s" % self.__state)
116 self.__f.write(args)
117
118 def unlink(self):
119 if self.__state != self.__STATE['OPEN']:
120 raise Error("Attempt to unlink file in state %s" % self.__state)
121 self.__unlink = True
122 self.__f.close()
123 self.__state = self.__STATE['NOT-APPLIED']
124
125 def close(self):
126 if self.__state != self.__STATE['OPEN']:
127 raise Error("Attempt to close file in state %s" % self.__state)
128
129 self.__f.close()
130 self.__state = self.__STATE['NOT-APPLIED']
131
132 def changed(self):
133 if self.__state != self.__STATE['NOT-APPLIED']:
134 raise Error("Attempt to compare file in state %s" % self.__state)
135
136 return True
137
138 def apply(self):
139 if self.__state != self.__STATE['NOT-APPLIED']:
140 raise Error("Attempt to apply configuration from state %s" % self.__state)
141
142 for child in self.__children:
143 child.apply()
144
145 log("Applying changes to %s configuration" % self.__fname)
146
147 # Remove previous backup.
148 if os.access(self.__oldpath, os.F_OK):
149 os.unlink(self.__oldpath)
150
151 # Save current configuration.
152 if os.access(self.__path, os.F_OK):
153 os.link(self.__path, self.__oldpath)
154 os.unlink(self.__path)
155
156 # Apply new configuration.
157 assert(os.path.exists(self.__newpath))
158 if not self.__unlink:
159 os.link(self.__newpath, self.__path)
160 else:
161 pass # implicit unlink of original file
162
163 # Remove temporary file.
164 os.unlink(self.__newpath)
165
166 self.__state = self.__STATE['APPLIED']
167
168 def revert(self):
169 if self.__state != self.__STATE['APPLIED']:
170 raise Error("Attempt to revert configuration from state %s" % self.__state)
171
172 for child in self.__children:
173 child.revert()
174
175 log("Reverting changes to %s configuration" % self.__fname)
176
177 # Remove existing new configuration
178 if os.access(self.__newpath, os.F_OK):
179 os.unlink(self.__newpath)
180
181 # Revert new configuration.
182 if os.access(self.__path, os.F_OK):
183 os.link(self.__path, self.__newpath)
184 os.unlink(self.__path)
185
186 # Revert to old configuration.
187 if os.access(self.__oldpath, os.F_OK):
188 os.link(self.__oldpath, self.__path)
189 os.unlink(self.__oldpath)
190
191 # Leave .*.xapi-new as an aid to debugging.
192
193 self.__state = self.__STATE['REVERTED']
194
195 def commit(self):
196 if self.__state != self.__STATE['APPLIED']:
197 raise Error("Attempt to commit configuration from state %s" % self.__state)
198
199 for child in self.__children:
200 child.commit()
201
202 log("Committing changes to %s configuration" % self.__fname)
203
204 if os.access(self.__oldpath, os.F_OK):
205 os.unlink(self.__oldpath)
206 if os.access(self.__newpath, os.F_OK):
207 os.unlink(self.__newpath)
208
209 self.__state = self.__STATE['COMMITTED']
210
211def debug_mode():
212 return output_directory is not None
213
214def log(s):
215 if debug_mode():
216 print >>sys.stderr, s
217 else:
218 syslog.syslog(s)
219
220def check_allowed(pif):
221 pifrec = db.get_pif_record(pif)
222 try:
223 f = open("/proc/ardence")
224 macline = filter(lambda x: x.startswith("HWaddr:"), f.readlines())
225 f.close()
226 if len(macline) == 1:
227 p = re.compile(".*\s%(MAC)s\s.*" % pifrec, re.IGNORECASE)
228 if p.match(macline[0]):
229 log("Skipping PVS device %(device)s (%(MAC)s)" % pifrec)
230 return False
231 except IOError:
232 pass
233 return True
234
235def interface_exists(i):
236 return os.path.exists("/sys/class/net/" + i)
237
2bb451b6
BP
238def get_netdev_mac(device):
239 try:
240 return read_first_line_of_file("/sys/class/net/%s/address" % device)
241 except:
242 # Probably no such device.
243 return None
244
245def get_netdev_tx_queue_len(device):
246 try:
247 return int(read_first_line_of_file("/sys/class/net/%s/tx_queue_len"
248 % device))
249 except:
250 # Probably no such device.
251 return None
252
253def get_netdev_by_mac(mac):
2bb451b6 254 for device in os.listdir("/sys/class/net"):
0ceda58c 255 dev_mac = get_netdev_mac(device)
9d04e270
BP
256 if (dev_mac and mac.lower() == dev_mac.lower() and
257 get_netdev_tx_queue_len(device)):
2bb451b6 258 return device
9d04e270 259 return None
2bb451b6 260
6dd3fad4
IC
261#
262# Helper functions for encoding/decoding database attributes to/from XML.
263#
264def str_to_xml(xml, parent, tag, val):
265 e = xml.createElement(tag)
266 parent.appendChild(e)
267 v = xml.createTextNode(val)
268 e.appendChild(v)
269def str_from_xml(n):
270 def getText(nodelist):
271 rc = ""
272 for node in nodelist:
273 if node.nodeType == node.TEXT_NODE:
274 rc = rc + node.data
275 return rc
276 return getText(n.childNodes).strip()
277
278
279def bool_to_xml(xml, parent, tag, val):
280 if val:
281 str_to_xml(xml, parent, tag, "True")
282 else:
283 str_to_xml(xml, parent, tag, "False")
284def bool_from_xml(n):
285 s = str_from_xml(n)
286 if s == "True":
287 return True
288 elif s == "False":
289 return False
290 else:
291 raise Error("Unknown boolean value %s" % s);
292
293def strlist_to_xml(xml, parent, ltag, itag, val):
294 e = xml.createElement(ltag)
295 parent.appendChild(e)
296 for v in val:
297 c = xml.createElement(itag)
298 e.appendChild(c)
299 cv = xml.createTextNode(v)
300 c.appendChild(cv)
301def strlist_from_xml(n, ltag, itag):
302 ret = []
303 for n in n.childNodes:
304 if n.nodeName == itag:
305 ret.append(str_from_xml(n))
306 return ret
307
308def otherconfig_to_xml(xml, parent, val, attrs):
309 otherconfig = xml.createElement("other_config")
310 parent.appendChild(otherconfig)
311 for n,v in val.items():
312 if not n in attrs:
313 raise Error("Unknown other-config attribute: %s" % n)
314 str_to_xml(xml, otherconfig, n, v)
315def otherconfig_from_xml(n, attrs):
316 ret = {}
317 for n in n.childNodes:
318 if n.nodeName in attrs:
319 ret[n.nodeName] = str_from_xml(n)
320 return ret
321
322#
323# Definitions of the database objects (and their attributes) used by interface-reconfigure.
324#
325# Each object is defined by a dictionary mapping an attribute name in
326# the xapi database to a tuple containing two items:
327# - a function which takes this attribute and encodes it as XML.
328# - a function which takes XML and decocdes it into a value.
329#
330# other-config attributes are specified as a simple array of strings
331
332PIF_XML_TAG = "pif"
333VLAN_XML_TAG = "vlan"
334BOND_XML_TAG = "bond"
335NETWORK_XML_TAG = "network"
336
171ed168
IC
337ETHTOOL_OTHERCONFIG_ATTRS = ['ethtool-%s' % x for x in 'autoneg', 'speed', 'duplex', 'rx', 'tx', 'sg', 'tso', 'ufo', 'gso' ]
338
6dd3fad4
IC
339PIF_ATTRS = { 'uuid': (str_to_xml,str_from_xml),
340 'management': (bool_to_xml,bool_from_xml),
341 'network': (str_to_xml,str_from_xml),
342 'device': (str_to_xml,str_from_xml),
343 'bond_master_of': (lambda x, p, t, v: strlist_to_xml(x, p, 'bond_master_of', 'slave', v),
344 lambda n: strlist_from_xml(n, 'bond_master_of', 'slave')),
345 'bond_slave_of': (str_to_xml,str_from_xml),
346 'VLAN': (str_to_xml,str_from_xml),
347 'VLAN_master_of': (str_to_xml,str_from_xml),
348 'VLAN_slave_of': (lambda x, p, t, v: strlist_to_xml(x, p, 'VLAN_slave_of', 'master', v),
349 lambda n: strlist_from_xml(n, 'VLAN_slave_Of', 'master')),
350 'ip_configuration_mode': (str_to_xml,str_from_xml),
351 'IP': (str_to_xml,str_from_xml),
352 'netmask': (str_to_xml,str_from_xml),
353 'gateway': (str_to_xml,str_from_xml),
354 'DNS': (str_to_xml,str_from_xml),
355 'MAC': (str_to_xml,str_from_xml),
356 'other_config': (lambda x, p, t, v: otherconfig_to_xml(x, p, v, PIF_OTHERCONFIG_ATTRS),
357 lambda n: otherconfig_from_xml(n, PIF_OTHERCONFIG_ATTRS)),
b88907e7
IC
358
359 # Special case: We write the current value
360 # PIF.currently-attached to the cache but since it will
361 # not be valid when we come to use the cache later
362 # (i.e. after a reboot) we always read it as False.
363 'currently_attached': (bool_to_xml, lambda n: False),
6dd3fad4 364 }
171ed168
IC
365
366PIF_OTHERCONFIG_ATTRS = [ 'domain', 'peerdns', 'defaultroute', 'mtu', 'static-routes' ] + \
367 [ 'bond-%s' % x for x in 'mode', 'miimon', 'downdelay', 'updelay', 'use_carrier' ] + \
368 ETHTOOL_OTHERCONFIG_ATTRS
369
6dd3fad4
IC
370VLAN_ATTRS = { 'uuid': (str_to_xml,str_from_xml),
371 'tagged_PIF': (str_to_xml,str_from_xml),
372 'untagged_PIF': (str_to_xml,str_from_xml),
373 }
171ed168 374
6dd3fad4
IC
375BOND_ATTRS = { 'uuid': (str_to_xml,str_from_xml),
376 'master': (str_to_xml,str_from_xml),
377 'slaves': (lambda x, p, t, v: strlist_to_xml(x, p, 'slaves', 'slave', v),
378 lambda n: strlist_from_xml(n, 'slaves', 'slave')),
379 }
380
381NETWORK_ATTRS = { 'uuid': (str_to_xml,str_from_xml),
382 'bridge': (str_to_xml,str_from_xml),
383 'PIFs': (lambda x, p, t, v: strlist_to_xml(x, p, 'PIFs', 'PIF', v),
384 lambda n: strlist_from_xml(n, 'PIFs', 'PIF')),
385 'other_config': (lambda x, p, t, v: otherconfig_to_xml(x, p, v, NETWORK_OTHERCONFIG_ATTRS),
386 lambda n: otherconfig_from_xml(n, NETWORK_OTHERCONFIG_ATTRS)),
387 }
171ed168
IC
388
389NETWORK_OTHERCONFIG_ATTRS = [ 'mtu', 'static-routes' ] + ETHTOOL_OTHERCONFIG_ATTRS
390
064af421 391class DatabaseCache(object):
c798b21c
IC
392 def __read_xensource_inventory(self):
393 filename = "/etc/xensource-inventory"
394 f = open(filename, "r")
395 lines = [x.strip("\n") for x in f.readlines()]
396 f.close()
397
398 defs = [ (l[:l.find("=")], l[(l.find("=") + 1):]) for l in lines ]
399 defs = [ (a, b.strip("'")) for (a,b) in defs ]
400
401 return dict(defs)
c798b21c
IC
402 def __pif_on_host(self,pif):
403 return self.__pifs.has_key(pif)
171ed168 404
c798b21c 405 def __get_pif_records_from_xapi(self, session, host):
171ed168
IC
406 self.__pifs = {}
407 for (p,rec) in session.xenapi.PIF.get_all_records().items():
408 if rec['host'] != host:
409 continue
410 self.__pifs[p] = {}
411 for f in PIF_ATTRS:
412 self.__pifs[p][f] = rec[f]
413 self.__pifs[p]['other_config'] = {}
414 for f in PIF_OTHERCONFIG_ATTRS:
415 if not rec['other_config'].has_key(f): continue
416 self.__pifs[p]['other_config'][f] = rec['other_config'][f]
c798b21c 417
d8ba4acf 418 def __get_vlan_records_from_xapi(self, session):
171ed168
IC
419 self.__vlans = {}
420 for v in session.xenapi.VLAN.get_all():
421 rec = session.xenapi.VLAN.get_record(v)
422 if not self.__pif_on_host(rec['untagged_PIF']):
423 continue
424 self.__vlans[v] = {}
425 for f in VLAN_ATTRS:
426 self.__vlans[v][f] = rec[f]
427
d8ba4acf 428 def __get_bond_records_from_xapi(self, session):
171ed168
IC
429 self.__bonds = {}
430 for b in session.xenapi.Bond.get_all():
431 rec = session.xenapi.Bond.get_record(b)
432 if not self.__pif_on_host(rec['master']):
433 continue
434 self.__bonds[b] = {}
435 for f in BOND_ATTRS:
436 self.__bonds[b][f] = rec[f]
c798b21c 437
d8ba4acf 438 def __get_network_records_from_xapi(self, session):
171ed168
IC
439 self.__networks = {}
440 for n in session.xenapi.network.get_all():
441 rec = session.xenapi.network.get_record(n)
442 self.__networks[n] = {}
443 for f in NETWORK_ATTRS:
5de7052b
IC
444 if f == "PIFs":
445 # drop PIFs on other hosts
446 self.__networks[n][f] = [p for p in rec[f] if self.__pif_on_host(p)]
447 else:
448 self.__networks[n][f] = rec[f]
171ed168
IC
449 self.__networks[n]['other_config'] = {}
450 for f in NETWORK_OTHERCONFIG_ATTRS:
451 if not rec['other_config'].has_key(f): continue
452 self.__networks[n]['other_config'][f] = rec['other_config'][f]
6dd3fad4
IC
453
454 def __to_xml(self, xml, parent, key, ref, rec, attrs):
455 """Encode a database object as XML"""
456 e = xml.createElement(key)
457 parent.appendChild(e)
458 if ref:
459 e.setAttribute('ref', ref)
460
461 for n,v in rec.items():
462 if attrs.has_key(n):
463 h,_ = attrs[n]
464 h(xml, e, n, v)
465 else:
466 raise Error("Unknown attribute %s" % n)
467 def __from_xml(self, e, attrs):
468 """Decode a database object from XML"""
469 ref = e.attributes['ref'].value
470 rec = {}
471 for n in e.childNodes:
472 if n.nodeName in attrs:
473 _,h = attrs[n.nodeName]
474 rec[n.nodeName] = h(n)
475 return (ref,rec)
476
064af421
BP
477 def __init__(self, session_ref=None, cache_file=None):
478 if session_ref and cache_file:
479 raise Error("can't specify session reference and cache file")
064af421
BP
480 if cache_file == None:
481 session = XenAPI.xapi_local()
482
483 if not session_ref:
484 log("No session ref given on command line, logging in.")
485 session.xenapi.login_with_password("root", "")
486 else:
487 session._session = session_ref
488
489 try:
c798b21c
IC
490
491 inventory = self.__read_xensource_inventory()
492 assert(inventory.has_key('INSTALLATION_UUID'))
493 log("host uuid is %s" % inventory['INSTALLATION_UUID'])
494
495 host = session.xenapi.host.get_by_uuid(inventory['INSTALLATION_UUID'])
496
497 self.__get_pif_records_from_xapi(session, host)
498
d8ba4acf
IC
499 self.__get_vlan_records_from_xapi(session)
500 self.__get_bond_records_from_xapi(session)
501 self.__get_network_records_from_xapi(session)
064af421
BP
502 finally:
503 if not session_ref:
504 session.xenapi.session.logout()
505 else:
506 log("Loading xapi database cache from %s" % cache_file)
064af421 507
6dd3fad4
IC
508 xml = parseXML(cache_file)
509
510 self.__pifs = {}
511 self.__bonds = {}
512 self.__vlans = {}
513 self.__networks = {}
064af421 514
6dd3fad4
IC
515 assert(len(xml.childNodes) == 1)
516 toplevel = xml.childNodes[0]
517
518 assert(toplevel.nodeName == "xenserver-network-configuration")
519
520 for n in toplevel.childNodes:
521 if n.nodeName == "#text":
522 pass
523 elif n.nodeName == PIF_XML_TAG:
524 (ref,rec) = self.__from_xml(n, PIF_ATTRS)
525 self.__pifs[ref] = rec
526 elif n.nodeName == BOND_XML_TAG:
527 (ref,rec) = self.__from_xml(n, BOND_ATTRS)
528 self.__bonds[ref] = rec
529 elif n.nodeName == VLAN_XML_TAG:
530 (ref,rec) = self.__from_xml(n, VLAN_ATTRS)
531 self.__vlans[ref] = rec
532 elif n.nodeName == NETWORK_XML_TAG:
533 (ref,rec) = self.__from_xml(n, NETWORK_ATTRS)
534 self.__networks[ref] = rec
535 else:
536 raise Error("Unknown XML element %s" % n.nodeName)
064af421 537
c798b21c 538 def save(self, cache_file):
6dd3fad4
IC
539
540 xml = getDOMImplementation().createDocument(
541 None, "xenserver-network-configuration", None)
542 for (ref,rec) in self.__pifs.items():
543 self.__to_xml(xml, xml.documentElement, PIF_XML_TAG, ref, rec, PIF_ATTRS)
544 for (ref,rec) in self.__bonds.items():
545 self.__to_xml(xml, xml.documentElement, BOND_XML_TAG, ref, rec, BOND_ATTRS)
546 for (ref,rec) in self.__vlans.items():
547 self.__to_xml(xml, xml.documentElement, VLAN_XML_TAG, ref, rec, VLAN_ATTRS)
548 for (ref,rec) in self.__networks.items():
549 self.__to_xml(xml, xml.documentElement, NETWORK_XML_TAG, ref, rec,
550 NETWORK_ATTRS)
551
064af421 552 f = open(cache_file, 'w')
6dd3fad4 553 f.write(xml.toprettyxml())
064af421
BP
554 f.close()
555
556 def get_pif_by_uuid(self, uuid):
557 pifs = map(lambda (ref,rec): ref,
558 filter(lambda (ref,rec): uuid == rec['uuid'],
559 self.__pifs.items()))
560 if len(pifs) == 0:
561 raise Error("Unknown PIF \"%s\"" % uuid)
562 elif len(pifs) > 1:
563 raise Error("Non-unique PIF \"%s\"" % uuid)
564
565 return pifs[0]
566
c798b21c 567 def get_pifs_by_device(self, device):
064af421 568 return map(lambda (ref,rec): ref,
c798b21c 569 filter(lambda (ref,rec): rec['device'] == device,
064af421
BP
570 self.__pifs.items()))
571
c798b21c 572 def get_pif_by_bridge(self, bridge):
064af421
BP
573 networks = map(lambda (ref,rec): ref,
574 filter(lambda (ref,rec): rec['bridge'] == bridge,
575 self.__networks.items()))
576 if len(networks) == 0:
577 raise Error("No matching network \"%s\"")
578
579 answer = None
580 for network in networks:
581 nwrec = self.get_network_record(network)
582 for pif in nwrec['PIFs']:
583 pifrec = self.get_pif_record(pif)
064af421 584 if answer:
c798b21c 585 raise Error("Multiple PIFs on host for network %s" % (bridge))
064af421
BP
586 answer = pif
587 if not answer:
c798b21c 588 raise Error("No PIF on host for network %s" % (bridge))
064af421
BP
589 return answer
590
591 def get_pif_record(self, pif):
592 if self.__pifs.has_key(pif):
593 return self.__pifs[pif]
6dd3fad4 594 raise Error("Unknown PIF \"%s\" (get_pif_record)" % pif)
064af421
BP
595 def get_all_pifs(self):
596 return self.__pifs
597 def pif_exists(self, pif):
598 return self.__pifs.has_key(pif)
599
c798b21c 600 def get_management_pif(self):
064af421
BP
601 """ Returns the management pif on host
602 """
603 all = self.get_all_pifs()
604 for pif in all:
605 pifrec = self.get_pif_record(pif)
c798b21c 606 if pifrec['management']: return pif
064af421
BP
607 return None
608
609 def get_network_record(self, network):
610 if self.__networks.has_key(network):
611 return self.__networks[network]
612 raise Error("Unknown network \"%s\"" % network)
613 def get_all_networks(self):
614 return self.__networks
615
616 def get_bond_record(self, bond):
617 if self.__bonds.has_key(bond):
618 return self.__bonds[bond]
619 else:
620 return None
621
622 def get_vlan_record(self, vlan):
623 if self.__vlans.has_key(vlan):
624 return self.__vlans[vlan]
625 else:
626 return None
627
628def bridge_name(pif):
629 """Return the bridge name associated with pif, or None if network is bridgeless"""
630 pifrec = db.get_pif_record(pif)
631 nwrec = db.get_network_record(pifrec['network'])
632
633 if nwrec['bridge']:
634 # TODO: sanity check that nwrec['bridgeless'] != 'true'
635 return nwrec['bridge']
636 else:
637 # TODO: sanity check that nwrec['bridgeless'] == 'true'
638 return None
639
640def interface_name(pif):
641 """Construct an interface name from the given PIF record."""
642
643 pifrec = db.get_pif_record(pif)
644
645 if pifrec['VLAN'] == '-1':
646 return pifrec['device']
647 else:
648 return "%(device)s.%(VLAN)s" % pifrec
649
650def datapath_name(pif):
651 """Return the OpenFlow datapath name associated with pif.
652For a non-VLAN PIF, the datapath name is the bridge name.
653For a VLAN PIF, the datapath name is the bridge name for the PIF's VLAN slave.
654(xapi will create a datapath named with the bridge name even though we won't
655use it.)
656"""
657
6dd3fad4 658
064af421
BP
659 pifrec = db.get_pif_record(pif)
660
661 if pifrec['VLAN'] == '-1':
662 return bridge_name(pif)
663 else:
664 return bridge_name(get_vlan_slave_of_pif(pif))
665
666def ipdev_name(pif):
667 """Return the the name of the network device that carries the
668IP configuration (if any) associated with pif.
669The ipdev name is the same as the bridge name.
670"""
671
672 pifrec = db.get_pif_record(pif)
673 return bridge_name(pif)
674
bc952bb3 675def get_physdev_pifs(pif):
2bb451b6
BP
676 """Return the PIFs for the physical network device(s) associated with pif.
677For a VLAN PIF, this is the VLAN slave's physical device PIF.
678For a bond master PIF, these are the bond slave PIFs.
679For a non-VLAN, non-bond master PIF, the PIF is its own physical device PIF.
064af421 680"""
064af421
BP
681 pifrec = db.get_pif_record(pif)
682
683 if pifrec['VLAN'] != '-1':
eeb569bf 684 return get_physdev_pifs(get_vlan_slave_of_pif(pif))
064af421 685 elif len(pifrec['bond_master_of']) != 0:
2bb451b6 686 return get_bond_slaves_of_pif(pif)
064af421 687 else:
2bb451b6
BP
688 return [pif]
689
bc952bb3 690def get_physdev_names(pif):
2bb451b6
BP
691 """Return the name(s) of the physical network device(s) associated with pif.
692For a VLAN PIF, the physical devices are the VLAN slave's physical devices.
693For a bond master PIF, the physical devices are the bond slaves.
694For a non-VLAN, non-bond master PIF, the physical device is the PIF itself.
695"""
696
bc952bb3 697 return [db.get_pif_record(phys)['device'] for phys in get_physdev_pifs(pif)]
064af421
BP
698
699def log_pif_action(action, pif):
700 pifrec = db.get_pif_record(pif)
6dd3fad4
IC
701 rec = {}
702 rec['uuid'] = pifrec['uuid']
703 rec['ip_configuration_mode'] = pifrec['ip_configuration_mode']
704 rec['action'] = action
705 rec['interface-name'] = interface_name(pif)
057fed2b 706 rec['message'] = "Bring %(action)s PIF %(uuid)s" % rec
6dd3fad4 707 log("%(message)s: %(interface-name)s configured as %(ip_configuration_mode)s" % rec)
064af421
BP
708
709def get_bond_masters_of_pif(pif):
710 """Returns a list of PIFs which are bond masters of this PIF"""
711
712 pifrec = db.get_pif_record(pif)
713
714 bso = pifrec['bond_slave_of']
715
716 # bond-slave-of is currently a single reference but in principle a
717 # PIF could be a member of several bonds which are not
718 # concurrently attached. Be robust to this possibility.
719 if not bso or bso == "OpaqueRef:NULL":
720 bso = []
721 elif not type(bso) == list:
722 bso = [bso]
723
724 bondrecs = [db.get_bond_record(bond) for bond in bso]
725 bondrecs = [rec for rec in bondrecs if rec]
726
727 return [bond['master'] for bond in bondrecs]
728
729def get_bond_slaves_of_pif(pif):
730 """Returns a list of PIFs which make up the given bonded pif."""
731
732 pifrec = db.get_pif_record(pif)
064af421
BP
733
734 bmo = pifrec['bond_master_of']
735 if len(bmo) > 1:
736 raise Error("Bond-master-of contains too many elements")
737
738 if len(bmo) == 0:
739 return []
740
741 bondrec = db.get_bond_record(bmo[0])
742 if not bondrec:
743 raise Error("No bond record for bond master PIF")
744
745 return bondrec['slaves']
746
747def get_vlan_slave_of_pif(pif):
748 """Find the PIF which is the VLAN slave of pif.
749
750Returns the 'physical' PIF underneath the a VLAN PIF @pif."""
751
752 pifrec = db.get_pif_record(pif)
753
754 vlan = pifrec['VLAN_master_of']
755 if not vlan or vlan == "OpaqueRef:NULL":
756 raise Error("PIF is not a VLAN master")
757
758 vlanrec = db.get_vlan_record(vlan)
759 if not vlanrec:
760 raise Error("No VLAN record found for PIF")
761
762 return vlanrec['tagged_PIF']
763
764def get_vlan_masters_of_pif(pif):
765 """Returns a list of PIFs which are VLANs on top of the given pif."""
766
767 pifrec = db.get_pif_record(pif)
768 vlans = [db.get_vlan_record(v) for v in pifrec['VLAN_slave_of']]
769 return [v['untagged_PIF'] for v in vlans if v and db.pif_exists(v['untagged_PIF'])]
770
771def interface_deconfigure_commands(interface):
772 # The use of [!0-9] keeps an interface of 'eth0' from matching
773 # VLANs attached to eth0 (such as 'eth0.123'), which are distinct
774 # interfaces.
775 return ['--del-match=bridge.*.port=%s' % interface,
776 '--del-match=bonding.%s.[!0-9]*' % interface,
777 '--del-match=bonding.*.slave=%s' % interface,
778 '--del-match=vlan.%s.[!0-9]*' % interface,
779 '--del-match=port.%s.[!0-9]*' % interface,
780 '--del-match=iface.%s.[!0-9]*' % interface]
781
782def run_command(command):
783 log("Running command: " + ' '.join(command))
784 if os.spawnl(os.P_WAIT, command[0], *command) != 0:
785 log("Command failed: " + ' '.join(command))
786 return False
787 return True
788
2bb451b6
BP
789def rename_netdev(old_name, new_name):
790 log("Changing the name of %s to %s" % (old_name, new_name))
791 run_command(['/sbin/ifconfig', old_name, 'down'])
792 if not run_command(['/sbin/ip', 'link', 'set', old_name,
793 'name', new_name]):
794 raise Error("Could not rename %s to %s" % (old_name, new_name))
795
796# Check whether 'pif' exists and has the correct MAC.
797# If not, try to find a device with the correct MAC and rename it.
798# 'already_renamed' is used to avoid infinite recursion.
799def remap_pif(pif, already_renamed=[]):
800 pifrec = db.get_pif_record(pif)
801 device = pifrec['device']
802 mac = pifrec['MAC']
803
804 # Is there a network device named 'device' at all?
805 device_exists = interface_exists(device)
806 if device_exists:
807 # Yes. Does it have MAC 'mac'?
808 found_mac = get_netdev_mac(device)
809 if found_mac and mac.lower() == found_mac.lower():
810 # Yes, everything checks out the way we want. Nothing to do.
811 return
812 else:
813 log("No network device %s" % device)
814
815 # What device has MAC 'mac'?
816 cur_device = get_netdev_by_mac(mac)
817 if not cur_device:
818 log("No network device has MAC %s" % mac)
819 return
820
821 # First rename 'device', if it exists, to get it out of the way
822 # for 'cur_device' to replace it.
823 if device_exists:
824 rename_netdev(device, "dev%d" % random.getrandbits(24))
825
826 # Rename 'cur_device' to 'device'.
827 rename_netdev(cur_device, device)
828
449776d8
BP
829def read_first_line_of_file(name):
830 file = None
831 try:
832 file = open(name, 'r')
833 return file.readline().rstrip('\n')
834 finally:
835 if file != None:
836 file.close()
837
064af421
BP
838def down_netdev(interface, deconfigure=True):
839 if not interface_exists(interface):
840 log("down_netdev: interface %s does not exist, ignoring" % interface)
841 return
064af421 842 if deconfigure:
064af421
BP
843 # Kill dhclient.
844 pidfile_name = '/var/run/dhclient-%s.pid' % interface
064af421 845 try:
449776d8 846 os.kill(int(read_first_line_of_file(pidfile_name)), signal.SIGTERM)
064af421
BP
847 except:
848 pass
064af421
BP
849
850 # Remove dhclient pidfile.
851 try:
852 os.remove(pidfile_name)
853 except:
854 pass
3cc42ebb
BP
855
856 run_command(["/sbin/ifconfig", interface, '0.0.0.0'])
857
858 run_command(["/sbin/ifconfig", interface, 'down'])
064af421
BP
859
860def up_netdev(interface):
861 run_command(["/sbin/ifconfig", interface, 'up'])
862
863def find_distinguished_pifs(pif):
864 """Returns the PIFs on host that own DNS and the default route.
865The peerdns pif will be the one with pif::other-config:peerdns=true, or the mgmt pif if none have this set.
866The gateway pif will be the one with pif::other-config:defaultroute=true, or the mgmt pif if none have this set.
867
868Note: we prune out the bond master pif (if it exists).
869This is because when we are called to bring up an interface with a bond master, it is implicit that
870we should bring down that master."""
871
872 pifrec = db.get_pif_record(pif)
064af421 873
c798b21c 874 pifs = [ __pif for __pif in db.get_all_pifs() if
064af421
BP
875 (not __pif in get_bond_masters_of_pif(pif)) ]
876
877 peerdns_pif = None
878 defaultroute_pif = None
879
880 # loop through all the pifs on this host looking for one with
881 # other-config:peerdns = true, and one with
882 # other-config:default-route=true
c798b21c 883 for __pif in pifs:
064af421
BP
884 __pifrec = db.get_pif_record(__pif)
885 __oc = __pifrec['other_config']
886 if __oc.has_key('peerdns') and __oc['peerdns'] == 'true':
887 if peerdns_pif == None:
888 peerdns_pif = __pif
889 else:
890 log('Warning: multiple pifs with "peerdns=true" - choosing %s and ignoring %s' % \
891 (db.get_pif_record(peerdns_pif)['device'], __pifrec['device']))
892 if __oc.has_key('defaultroute') and __oc['defaultroute'] == 'true':
893 if defaultroute_pif == None:
894 defaultroute_pif = __pif
895 else:
896 log('Warning: multiple pifs with "defaultroute=true" - choosing %s and ignoring %s' % \
897 (db.get_pif_record(defaultroute_pif)['device'], __pifrec['device']))
898
899 # If no pif is explicitly specified then use the mgmt pif for peerdns/defaultroute
900 if peerdns_pif == None:
901 peerdns_pif = management_pif
902 if defaultroute_pif == None:
903 defaultroute_pif = management_pif
904
905 return peerdns_pif, defaultroute_pif
906
05e2ad78
BP
907def run_ethtool(device, oc):
908 # Run "ethtool -s" if there are any settings.
064af421
BP
909 settings = []
910 if oc.has_key('ethtool-speed'):
911 val = oc['ethtool-speed']
912 if val in ["10", "100", "1000"]:
913 settings += ['speed', val]
914 else:
915 log("Invalid value for ethtool-speed = %s. Must be 10|100|1000." % val)
916 if oc.has_key('ethtool-duplex'):
917 val = oc['ethtool-duplex']
918 if val in ["10", "100", "1000"]:
919 settings += ['duplex', 'val']
920 else:
921 log("Invalid value for ethtool-duplex = %s. Must be half|full." % val)
922 if oc.has_key('ethtool-autoneg'):
923 val = oc['ethtool-autoneg']
924 if val in ["true", "on"]:
925 settings += ['autoneg', 'on']
926 elif val in ["false", "off"]:
927 settings += ['autoneg', 'off']
928 else:
929 log("Invalid value for ethtool-autoneg = %s. Must be on|true|off|false." % val)
05e2ad78
BP
930 if settings:
931 run_command(['/sbin/ethtool', '-s', device] + settings)
064af421 932
05e2ad78 933 # Run "ethtool -K" if there are any offload settings.
064af421
BP
934 offload = []
935 for opt in ("rx", "tx", "sg", "tso", "ufo", "gso"):
936 if oc.has_key("ethtool-" + opt):
937 val = oc["ethtool-" + opt]
938 if val in ["true", "on"]:
939 offload += [opt, 'on']
940 elif val in ["false", "off"]:
941 offload += [opt, 'off']
942 else:
943 log("Invalid value for ethtool-%s = %s. Must be on|true|off|false." % (opt, val))
05e2ad78
BP
944 if offload:
945 run_command(['/sbin/ethtool', '-K', device] + offload)
064af421 946
05e2ad78
BP
947def mtu_setting(oc):
948 if oc.has_key('mtu'):
949 try:
950 int(oc['mtu']) # Check that the value is an integer
951 return ['mtu', oc['mtu']]
952 except ValueError, x:
953 log("Invalid value for mtu = %s" % mtu)
954 return []
064af421 955
88acec3b 956def configure_local_port(pif):
064af421
BP
957 pifrec = db.get_pif_record(pif)
958 datapath = datapath_name(pif)
959 ipdev = ipdev_name(pif)
960
064af421
BP
961 nw = pifrec['network']
962 nwrec = db.get_network_record(nw)
963
c87d1024
BP
964 pif_oc = pifrec['other_config']
965 nw_oc = nwrec['other_config']
966
967 # IP (except DHCP) and MTU.
064af421
BP
968 ifconfig_argv = ['/sbin/ifconfig', ipdev, 'up']
969 gateway = ''
970 if pifrec['ip_configuration_mode'] == "DHCP":
971 pass
972 elif pifrec['ip_configuration_mode'] == "Static":
973 ifconfig_argv += [pifrec['IP']]
974 ifconfig_argv += ['netmask', pifrec['netmask']]
975 gateway = pifrec['gateway']
976 elif pifrec['ip_configuration_mode'] == "None":
977 # Nothing to do.
978 pass
979 else:
980 raise Error("Unknown IP-configuration-mode %s" % pifrec['ip_configuration_mode'])
c87d1024 981 ifconfig_argv += mtu_setting(nw_oc)
064af421
BP
982 run_command(ifconfig_argv)
983
984 (peerdns_pif, defaultroute_pif) = find_distinguished_pifs(pif)
985
c87d1024 986 # /etc/resolv.conf
064af421
BP
987 if peerdns_pif == pif:
988 f = ConfigurationFile('resolv.conf', "/etc")
c87d1024
BP
989 if pif_oc.has_key('domain'):
990 f.write("search %s\n" % pif_oc['domain'])
064af421
BP
991 for dns in pifrec['DNS'].split(","):
992 f.write("nameserver %s\n" % dns)
993 f.close()
994 f.apply()
995 f.commit()
996
c87d1024 997 # Routing.
064af421
BP
998 if defaultroute_pif == pif and gateway != '':
999 run_command(['/sbin/ip', 'route', 'replace', 'default',
1000 'via', gateway, 'dev', ipdev])
c87d1024
BP
1001 if nw_oc.has_key('static-routes'):
1002 for line in nw_oc['static-routes'].split(','):
064af421
BP
1003 network, masklen, gateway = line.split('/')
1004 run_command(['/sbin/ip', 'route', 'add',
8f749dac 1005 '%s/%s' % (network, masklen), 'via', gateway,
064af421
BP
1006 'dev', ipdev])
1007
05e2ad78 1008 # Ethtool.
c87d1024 1009 run_ethtool(ipdev, nw_oc)
064af421 1010
c87d1024 1011 # DHCP.
064af421
BP
1012 if pifrec['ip_configuration_mode'] == "DHCP":
1013 print
1014 print "Determining IP information for %s..." % ipdev,
1015 argv = ['/sbin/dhclient', '-q',
1016 '-lf', '/var/lib/dhclient/dhclient-%s.leases' % ipdev,
1017 '-pf', '/var/run/dhclient-%s.pid' % ipdev,
1018 ipdev]
1019 if run_command(argv):
1020 print 'done.'
1021 else:
1022 print 'failed.'
1023
88acec3b
BP
1024def configure_physdev(pif):
1025 pifrec = db.get_pif_record(pif)
1026 device = pifrec['device']
1027 oc = pifrec['other_config']
1028
1029 run_command(['/sbin/ifconfig', device, 'up'] + mtu_setting(oc))
1030 run_ethtool(device, oc)
1031
064af421 1032def modify_config(commands):
eaa3c7e8 1033 run_command(['/usr/bin/ovs-cfg-mod', '-vANY:console:emer',
064af421
BP
1034 '-F', '/etc/ovs-vswitchd.conf']
1035 + commands + ['-c'])
1036 run_command(['/sbin/service', 'vswitch', 'reload'])
1037
1038def is_bond_pif(pif):
1039 pifrec = db.get_pif_record(pif)
1040 return len(pifrec['bond_master_of']) != 0
1041
1042def configure_bond(pif):
1043 pifrec = db.get_pif_record(pif)
1044 interface = interface_name(pif)
1045 ipdev = ipdev_name(pif)
1046 datapath = datapath_name(pif)
bc952bb3 1047 physdev_names = get_physdev_names(pif)
064af421
BP
1048
1049 argv = ['--del-match=bonding.%s.[!0-9]*' % interface]
1050 argv += ["--add=bonding.%s.slave=%s" % (interface, slave)
bc952bb3 1051 for slave in physdev_names]
d2cd45db 1052 argv += ['--add=bonding.%s.fake-iface=true' % interface]
064af421 1053
58b7527e
BP
1054 if pifrec['MAC'] != "":
1055 argv += ['--add=port.%s.mac=%s' % (interface, pifrec['MAC'])]
1056
064af421
BP
1057 # Bonding options.
1058 bond_options = {
1059 "mode": "balance-slb",
1060 "miimon": "100",
1061 "downdelay": "200",
1062 "updelay": "31000",
1063 "use_carrier": "1",
1064 }
1065 # override defaults with values from other-config whose keys
1066 # being with "bond-"
1067 oc = pifrec['other_config']
1068 overrides = filter(lambda (key,val):
1069 key.startswith("bond-"), oc.items())
1070 overrides = map(lambda (key,val): (key[5:], val), overrides)
1071 bond_options.update(overrides)
1072 for (name,val) in bond_options.items():
1073 argv += ["--add=bonding.%s.%s=%s" % (interface, name, val)]
1074 return argv
1075
1076def action_up(pif):
1077 pifrec = db.get_pif_record(pif)
1078
1079 bridge = bridge_name(pif)
1080 interface = interface_name(pif)
1081 ipdev = ipdev_name(pif)
1082 datapath = datapath_name(pif)
bc952bb3
BP
1083 physdev_pifs = get_physdev_pifs(pif)
1084 physdev_names = get_physdev_names(pif)
064af421
BP
1085 vlan_slave = None
1086 if pifrec['VLAN'] != '-1':
1087 vlan_slave = get_vlan_slave_of_pif(pif)
1088 if vlan_slave and is_bond_pif(vlan_slave):
1089 bond_master = vlan_slave
1090 elif is_bond_pif(pif):
1091 bond_master = pif
1092 else:
1093 bond_master = None
8826590a
BP
1094 if bond_master:
1095 bond_slaves = get_bond_slaves_of_pif(bond_master)
1096 else:
1097 bond_slaves = []
064af421
BP
1098 bond_masters = get_bond_masters_of_pif(pif)
1099
1100 # Support "rpm -e vswitch" gracefully by keeping Centos configuration
1101 # files up-to-date, even though we don't use them or need them.
1102 f = configure_pif(pif)
1103 mode = pifrec['ip_configuration_mode']
1104 if bridge:
1105 log("Configuring %s using %s configuration" % (bridge, mode))
1106 br = open_network_ifcfg(pif)
1107 configure_network(pif, br)
1108 br.close()
1109 f.attach_child(br)
1110 else:
1111 log("Configuring %s using %s configuration" % (interface, mode))
1112 configure_network(pif, f)
1113 f.close()
1114 for master in bond_masters:
1115 master_bridge = bridge_name(master)
1116 removed = unconfigure_pif(master)
1117 f.attach_child(removed)
1118 if master_bridge:
1119 removed = open_network_ifcfg(master)
1120 log("Unlinking stale file %s" % removed.path())
1121 removed.unlink()
1122 f.attach_child(removed)
1123
1124 # /etc/xensource/scripts/vif needs to know where to add VIFs.
1125 if vlan_slave:
e44d1844
IC
1126 if not os.path.exists(vswitch_state_dir):
1127 os.mkdir(vswitch_state_dir)
1128 br = ConfigurationFile("br-%s" % bridge, vswitch_state_dir)
064af421
BP
1129 br.write("VLAN_SLAVE=%s\n" % datapath)
1130 br.write("VLAN_VID=%s\n" % pifrec['VLAN'])
1131 br.close()
1132 f.attach_child(br)
1133
1134 # Update all configuration files (both ours and Centos's).
1135 f.apply()
1136 f.commit()
1137
2bb451b6
BP
1138 # Check the MAC address of each network device and remap if
1139 # necessary to make names match our expectations.
bc952bb3 1140 for physdev_pif in physdev_pifs:
2bb451b6
BP
1141 remap_pif(physdev_pif)
1142
064af421
BP
1143 # "ifconfig down" the network device and delete its IP address, etc.
1144 down_netdev(ipdev)
bc952bb3
BP
1145 for physdev_name in physdev_names:
1146 down_netdev(physdev_name)
064af421 1147
7a3696ad
BP
1148 # If we are bringing up a bond, remove IP addresses from the
1149 # slaves (because we are implicitly being asked to take them down).
1150 #
1151 # Conversely, if we are bringing up an interface that has bond
1152 # masters, remove IP addresses from the bond master (because we
1153 # are implicitly being asked to take it down).
1154 for bond_pif in bond_slaves + bond_masters:
1155 run_command(["/sbin/ifconfig", ipdev_name(bond_pif), '0.0.0.0'])
1156
064af421 1157 # Remove all keys related to pif and any bond masters linked to PIF.
bc952bb3 1158 del_ports = [ipdev] + physdev_names + bond_masters
064af421
BP
1159 if vlan_slave and bond_master:
1160 del_ports += [interface_name(bond_master)]
1161
1162 # What ports do we need to add to the datapath?
1163 #
1164 # We definitely need the ipdev, and ordinarily we want the
1165 # physical devices too, but for bonds we need the bond as bridge
1166 # port.
1167 add_ports = [ipdev, datapath]
1168 if not bond_master:
bc952bb3 1169 add_ports += physdev_names
064af421
BP
1170 else:
1171 add_ports += [interface_name(bond_master)]
1172
1173 # What ports do we need to delete?
1174 #
1175 # - All the ports that we add, to avoid duplication and to drop
1176 # them from another datapath in case they're misassigned.
1177 #
1178 # - The physical devices, since they will either be in add_ports
1179 # or added to the bonding device (see below).
1180 #
1181 # - The bond masters for pif. (Ordinarily pif shouldn't have any
1182 # bond masters. If it does then interface-reconfigure is
1183 # implicitly being asked to take them down.)
bc952bb3 1184 del_ports = add_ports + physdev_names + bond_masters
064af421
BP
1185
1186 # What networks does this datapath carry?
1187 #
1188 # - The network corresponding to the datapath's PIF.
1189 #
1190 # - The networks corresponding to any VLANs attached to the
1191 # datapath's PIF.
1192 network_uuids = []
f134604b 1193 for nwpif in db.get_pifs_by_device(pifrec['device']):
064af421
BP
1194 net = db.get_pif_record(nwpif)['network']
1195 network_uuids += [db.get_network_record(net)['uuid']]
1196
8826590a
BP
1197 # Bring up bond slaves early, because ovs-vswitchd initially
1198 # enables or disables bond slaves based on whether carrier is
1199 # detected when they are added, and a network device that is down
1200 # always reports "no carrier".
88acec3b 1201 bond_slave_physdev_pifs = []
8826590a 1202 for slave in bond_slaves:
88acec3b
BP
1203 bond_slave_physdev_pifs += get_physdev_pifs(slave)
1204 for slave_physdev_pif in set(bond_slave_physdev_pifs):
1205 configure_physdev(slave_physdev_pif)
8826590a 1206
064af421
BP
1207 # Now modify the ovs-vswitchd config file.
1208 argv = []
1209 for port in set(del_ports):
1210 argv += interface_deconfigure_commands(port)
1211 for port in set(add_ports):
1212 argv += ['--add=bridge.%s.port=%s' % (datapath, port)]
1213 if vlan_slave:
1214 argv += ['--add=vlan.%s.tag=%s' % (ipdev, pifrec['VLAN'])]
1215 argv += ['--add=iface.%s.internal=true' % (ipdev)]
1216
1217 # xapi creates a bridge by the name of the ipdev and requires
1218 # that the IP address will be on it. We need to delete this
1219 # bridge because we need that device to be a member of our
1220 # datapath.
1221 argv += ['--del-match=bridge.%s.[!0-9]*' % ipdev]
1222
1223 # xapi insists that its attempts to create the bridge succeed,
1224 # so force that to happen.
1225 argv += ['--add=iface.%s.fake-bridge=true' % (ipdev)]
1226 else:
1227 try:
e44d1844 1228 os.unlink("%s/br-%s" % (vswitch_state_dir, bridge))
064af421
BP
1229 except OSError:
1230 pass
1231 argv += ['--del-match=bridge.%s.xs-network-uuids=*' % datapath]
1232 argv += ['--add=bridge.%s.xs-network-uuids=%s' % (datapath, uuid)
1233 for uuid in set(network_uuids)]
1234 if bond_master:
1235 argv += configure_bond(bond_master)
1236 modify_config(argv)
1237
8826590a
BP
1238 # Bring up VLAN slave, plus physical devices other than bond
1239 # slaves (which we brought up earlier).
fa2bec94
IC
1240 # XXX need to bring up bond itself.
1241 # XXX need to set MAC address on fake bridge
064af421
BP
1242 if vlan_slave:
1243 up_netdev(ipdev_name(vlan_slave))
88acec3b
BP
1244 for physdev_pif in set(physdev_pifs) - set(bond_slave_physdev_pifs):
1245 configure_physdev(physdev_pif)
064af421 1246
88acec3b
BP
1247 # Configure network device for local port.
1248 configure_local_port(pif)
0d7e8aac 1249
064af421
BP
1250 # Update /etc/issue (which contains the IP address of the management interface)
1251 os.system("/sbin/update-issue")
7a3696ad
BP
1252
1253 if bond_slaves:
1254 # There seems to be a race somewhere: without this sleep, using
1255 # XenCenter to create a bond that becomes the management interface
1256 # fails with "The underlying connection was closed: A connection that
1257 # was expected to be kept alive was closed by the server." on every
1258 # second or third try, even though /var/log/messages doesn't show
1259 # anything unusual.
1260 #
1261 # The race is probably present even without vswitch, but bringing up a
1262 # bond without vswitch involves a built-in pause of 10 seconds or more
1263 # to wait for the bond to transition from learning to forwarding state.
1264 time.sleep(5)
064af421
BP
1265
1266def action_down(pif):
1267 rec = db.get_pif_record(pif)
1268 interface = interface_name(pif)
1269 bridge = bridge_name(pif)
1270 ipdev = ipdev_name(pif)
1271
1272 # Support "rpm -e vswitch" gracefully by keeping Centos configuration
1273 # files up-to-date, even though we don't use them or need them.
1274 f = unconfigure_pif(pif)
1275 if bridge:
1276 br = open_network_ifcfg(pif)
1277 log("Unlinking stale file %s" % br.path())
1278 br.unlink()
1279 f.attach_child(br)
1280 try:
1281 f.apply()
1282 f.commit()
1283 except Error, e:
1284 log("action_down failed to apply changes: %s" % e.msg)
1285 f.revert()
1286 raise
1287
1288 argv = []
1289 if rec['VLAN'] != '-1':
1290 # Get rid of the VLAN device itself.
1291 down_netdev(ipdev)
1292 argv += interface_deconfigure_commands(ipdev)
1293
1294 # If the VLAN's slave is attached, stop here.
1295 slave = get_vlan_slave_of_pif(pif)
1296 if db.get_pif_record(slave)['currently_attached']:
1297 log("VLAN slave is currently attached")
1298 modify_config(argv)
1299 return
1300
1301 # If the VLAN's slave has other VLANs that are attached, stop here.
1302 masters = get_vlan_masters_of_pif(slave)
1303 for m in masters:
1304 if m != pif and db.get_pif_record(m)['currently_attached']:
1305 log("VLAN slave has other master %s" % interface_naem(m))
1306 modify_config(argv)
1307 return
1308
1309 # Otherwise, take down the VLAN's slave too.
1310 log("No more masters, bring down vlan slave %s" % interface_name(slave))
1311 pif = slave
1312 else:
1313 # Stop here if this PIF has attached VLAN masters.
1314 vlan_masters = get_vlan_masters_of_pif(pif)
1315 log("VLAN masters of %s - %s" % (rec['device'], [interface_name(m) for m in vlan_masters]))
1316 for m in vlan_masters:
1317 if db.get_pif_record(m)['currently_attached']:
fa2bec94 1318 # XXX remove IP address
064af421
BP
1319 log("Leaving %s up due to currently attached VLAN master %s" % (interface, interface_name(m)))
1320 return
1321
1322 # pif is now either a bond or a physical device which needs to be
1323 # brought down. pif might have changed so re-check all its attributes.
1324 rec = db.get_pif_record(pif)
1325 interface = interface_name(pif)
1326 bridge = bridge_name(pif)
1327 ipdev = ipdev_name(pif)
1328
1329
1330 bond_slaves = get_bond_slaves_of_pif(pif)
1331 log("bond slaves of %s - %s" % (rec['device'], [interface_name(s) for s in bond_slaves]))
1332 for slave in bond_slaves:
1333 slave_interface = interface_name(slave)
1334 log("bring down bond slave %s" % slave_interface)
1335 argv += interface_deconfigure_commands(slave_interface)
1336 down_netdev(slave_interface)
1337
1338 argv += interface_deconfigure_commands(ipdev)
1339 down_netdev(ipdev)
1340
1341 argv += ['--del-match', 'bridge.%s.*' % datapath_name(pif)]
1342 argv += ['--del-match', 'bonding.%s.[!0-9]*' % interface]
1343 modify_config(argv)
1344
1345def action_rewrite(pif):
1346 # Support "rpm -e vswitch" gracefully by keeping Centos configuration
1347 # files up-to-date, even though we don't use them or need them.
1348 pifrec = db.get_pif_record(pif)
1349 f = configure_pif(pif)
1350 interface = interface_name(pif)
1351 bridge = bridge_name(pif)
1352 mode = pifrec['ip_configuration_mode']
1353 if bridge:
1354 log("Configuring %s using %s configuration" % (bridge, mode))
1355 br = open_network_ifcfg(pif)
1356 configure_network(pif, br)
1357 br.close()
1358 f.attach_child(br)
1359 else:
1360 log("Configuring %s using %s configuration" % (interface, mode))
1361 configure_network(pif, f)
1362 f.close()
1363 try:
1364 f.apply()
1365 f.commit()
1366 except Error, e:
1367 log("failed to apply changes: %s" % e.msg)
1368 f.revert()
1369 raise
1370
1371 # We have no code of our own to run here.
1372 pass
1373
fa2bec94
IC
1374def action_force_rewrite(interface, config):
1375 raise Error("Force rewrite is not implemented yet.")
1376
064af421
BP
1377def main(argv=None):
1378 global output_directory, management_pif
1379
1380 session = None
1381 pif_uuid = None
1382 pif = None
1383
1384 force_interface = None
1385 force_management = False
1386
1387 if argv is None:
1388 argv = sys.argv
1389
1390 try:
1391 try:
1392 shortops = "h"
1393 longops = [ "output-directory=",
1394 "pif=", "pif-uuid=",
1395 "session=",
1396 "force=",
1397 "force-interface=",
1398 "management",
064af421
BP
1399 "device=", "mode=", "ip=", "netmask=", "gateway=",
1400 "help" ]
1401 arglist, args = getopt.gnu_getopt(argv[1:], shortops, longops)
1402 except getopt.GetoptError, msg:
1403 raise Usage(msg)
1404
1405 force_rewrite_config = {}
1406
1407 for o,a in arglist:
1408 if o == "--output-directory":
1409 output_directory = a
1410 elif o == "--pif":
1411 pif = a
1412 elif o == "--pif-uuid":
1413 pif_uuid = a
1414 elif o == "--session":
1415 session = a
1416 elif o == "--force-interface" or o == "--force":
1417 force_interface = a
1418 elif o == "--management":
1419 force_management = True
1420 elif o in ["--device", "--mode", "--ip", "--netmask", "--gateway"]:
1421 force_rewrite_config[o[2:]] = a
1422 elif o == "-h" or o == "--help":
1423 print __doc__ % {'command-name': os.path.basename(argv[0])}
1424 return 0
1425
1426 if not debug_mode():
1427 syslog.openlog(os.path.basename(argv[0]))
1428 log("Called as " + str.join(" ", argv))
1429 if len(args) < 1:
1430 raise Usage("Required option <action> not present")
1431 if len(args) > 1:
1432 raise Usage("Too many arguments")
1433
1434 action = args[0]
fa2bec94
IC
1435
1436 if not action in ["up", "down", "rewrite", "rewrite-configuration"]:
1437 raise Usage("Unknown action \"%s\"" % action)
1438
064af421
BP
1439 # backwards compatibility
1440 if action == "rewrite-configuration": action = "rewrite"
1441
1442 if output_directory and ( session or pif ):
1443 raise Usage("--session/--pif cannot be used with --output-directory")
1444 if ( session or pif ) and pif_uuid:
1445 raise Usage("--session/--pif and --pif-uuid are mutually exclusive.")
1446 if ( session and not pif ) or ( not session and pif ):
1447 raise Usage("--session and --pif must be used together.")
1448 if force_interface and ( session or pif or pif_uuid ):
1449 raise Usage("--force is mutually exclusive with --session, --pif and --pif-uuid")
fa2bec94
IC
1450 if force_interface == "all" and action != "down":
1451 raise Usage("\"--force all\" only valid for down action")
064af421
BP
1452 if len(force_rewrite_config) and not (force_interface and action == "rewrite"):
1453 raise Usage("\"--force rewrite\" needed for --device, --mode, --ip, --netmask, and --gateway")
1454
1455 global db
1456 if force_interface:
1457 log("Force interface %s %s" % (force_interface, action))
1458
1459 if action == "rewrite":
1460 action_force_rewrite(force_interface, force_rewrite_config)
fa2bec94
IC
1461 elif action in ["up", "down"]:
1462 if action == "down" and force_interface == "all":
1463 raise Error("Force all interfaces down not implemented yet")
1464
064af421 1465 db = DatabaseCache(cache_file=dbcache_file)
c798b21c
IC
1466 pif = db.get_pif_by_bridge(force_interface)
1467 management_pif = db.get_management_pif()
064af421
BP
1468
1469 if action == "up":
1470 action_up(pif)
1471 elif action == "down":
1472 action_down(pif)
fa2bec94
IC
1473 else:
1474 raise Error("Unknown action %s" % action)
064af421
BP
1475 else:
1476 db = DatabaseCache(session_ref=session)
1477
1478 if pif_uuid:
1479 pif = db.get_pif_by_uuid(pif_uuid)
064af421 1480
057fed2b
BP
1481 if action == "rewrite" and not pif:
1482 pass
064af421 1483 else:
057fed2b
BP
1484 if not pif:
1485 raise Usage("No PIF given")
064af421 1486
057fed2b
BP
1487 if force_management:
1488 # pif is going to be the management pif
1489 management_pif = pif
1490 else:
1491 # pif is not going to be the management pif.
1492 # Search DB cache for pif on same host with management=true
1493 pifrec = db.get_pif_record(pif)
1494 management_pif = db.get_management_pif()
064af421 1495
057fed2b 1496 log_pif_action(action, pif)
064af421 1497
057fed2b
BP
1498 if not check_allowed(pif):
1499 return 0
1500
1501 if action == "up":
1502 action_up(pif)
1503 elif action == "down":
1504 action_down(pif)
1505 elif action == "rewrite":
1506 action_rewrite(pif)
1507 else:
fa2bec94 1508 raise Error("Unknown action %s" % action)
064af421
BP
1509
1510 # Save cache.
c798b21c 1511 db.save(dbcache_file)
064af421
BP
1512
1513 except Usage, err:
1514 print >>sys.stderr, err.msg
1515 print >>sys.stderr, "For help use --help."
1516 return 2
1517 except Error, err:
1518 log(err.msg)
1519 return 1
1520
1521 return 0
1522\f
1523# The following code allows interface-reconfigure to keep Centos
1524# network configuration files up-to-date, even though the vswitch
1525# never uses them. In turn, that means that "rpm -e vswitch" does not
1526# have to update any configuration files.
1527
1528def configure_ethtool(oc, f):
1529 # Options for "ethtool -s"
1530 settings = None
1531 setting_opts = ["autoneg", "speed", "duplex"]
1532 # Options for "ethtool -K"
1533 offload = None
1534 offload_opts = ["rx", "tx", "sg", "tso", "ufo", "gso"]
1535
1536 for opt in [opt for opt in setting_opts + offload_opts if oc.has_key("ethtool-" + opt)]:
1537 val = oc["ethtool-" + opt]
1538
1539 if opt in ["speed"]:
1540 if val in ["10", "100", "1000"]:
1541 val = "speed " + val
1542 else:
1543 log("Invalid value for ethtool-speed = %s. Must be 10|100|1000." % val)
1544 val = None
1545 elif opt in ["duplex"]:
1546 if val in ["half", "full"]:
1547 val = "duplex " + val
1548 else:
1549 log("Invalid value for ethtool-duplex = %s. Must be half|full." % val)
1550 val = None
1551 elif opt in ["autoneg"] + offload_opts:
1552 if val in ["true", "on"]:
1553 val = opt + " on"
1554 elif val in ["false", "off"]:
1555 val = opt + " off"
1556 else:
1557 log("Invalid value for ethtool-%s = %s. Must be on|true|off|false." % (opt, val))
1558 val = None
1559
1560 if opt in setting_opts:
1561 if val and settings:
1562 settings = settings + " " + val
1563 else:
1564 settings = val
1565 elif opt in offload_opts:
1566 if val and offload:
1567 offload = offload + " " + val
1568 else:
1569 offload = val
1570
1571 if settings:
1572 f.write("ETHTOOL_OPTS=\"%s\"\n" % settings)
1573 if offload:
1574 f.write("ETHTOOL_OFFLOAD_OPTS=\"%s\"\n" % offload)
1575
1576def configure_mtu(oc, f):
1577 if not oc.has_key('mtu'):
1578 return
1579
1580 try:
1581 mtu = int(oc['mtu'])
1582 f.write("MTU=%d\n" % mtu)
1583 except ValueError, x:
1584 log("Invalid value for mtu = %s" % mtu)
1585
1586def configure_static_routes(interface, oc, f):
1587 """Open a route-<interface> file for static routes.
1588
1589 Opens the static routes configuration file for interface and writes one
1590 line for each route specified in the network's other config "static-routes" value.
1591 E.g. if
1592 interface ( RO): xenbr1
1593 other-config (MRW): static-routes: 172.16.0.0/15/192.168.0.3,172.18.0.0/16/192.168.0.4;...
1594
1595 Then route-xenbr1 should be
1596 172.16.0.0/15 via 192.168.0.3 dev xenbr1
1597 172.18.0.0/16 via 192.168.0.4 dev xenbr1
1598 """
1599 fname = "route-%s" % interface
1600 if oc.has_key('static-routes'):
1601 # The key is present - extract comma seperates entries
1602 lines = oc['static-routes'].split(',')
1603 else:
1604 # The key is not present, i.e. there are no static routes
1605 lines = []
1606
1607 child = ConfigurationFile(fname)
1608 child.write("# DO NOT EDIT: This file (%s) was autogenerated by %s\n" % \
1609 (os.path.basename(child.path()), os.path.basename(sys.argv[0])))
1610
1611 try:
1612 for l in lines:
1613 network, masklen, gateway = l.split('/')
1614 child.write("%s/%s via %s dev %s\n" % (network, masklen, gateway, interface))
1615
1616 f.attach_child(child)
1617 child.close()
1618
1619 except ValueError, e:
1620 log("Error in other-config['static-routes'] format for network %s: %s" % (interface, e))
1621
1622def __open_ifcfg(interface):
1623 """Open a network interface configuration file.
1624
1625 Opens the configuration file for interface, writes a header and
1626 common options and returns the file object.
1627 """
1628 fname = "ifcfg-%s" % interface
1629 f = ConfigurationFile(fname)
1630
1631 f.write("# DO NOT EDIT: This file (%s) was autogenerated by %s\n" % \
1632 (os.path.basename(f.path()), os.path.basename(sys.argv[0])))
1633 f.write("XEMANAGED=yes\n")
1634 f.write("DEVICE=%s\n" % interface)
1635 f.write("ONBOOT=no\n")
1636
1637 return f
1638
1639def open_network_ifcfg(pif):
1640 bridge = bridge_name(pif)
1641 interface = interface_name(pif)
1642 if bridge:
1643 return __open_ifcfg(bridge)
1644 else:
1645 return __open_ifcfg(interface)
1646
1647
1648def open_pif_ifcfg(pif):
1649 pifrec = db.get_pif_record(pif)
1650
1651 log("Configuring %s (%s)" % (interface_name(pif), pifrec['MAC']))
1652
1653 f = __open_ifcfg(interface_name(pif))
1654
1655 if pifrec.has_key('other_config'):
1656 configure_ethtool(pifrec['other_config'], f)
1657 configure_mtu(pifrec['other_config'], f)
1658
1659 return f
1660
1661def configure_network(pif, f):
1662 """Write the configuration file for a network.
1663
1664 Writes configuration derived from the network object into the relevant
1665 ifcfg file. The configuration file is passed in, but if the network is
1666 bridgeless it will be ifcfg-<interface>, otherwise it will be ifcfg-<bridge>.
1667
1668 This routine may also write ifcfg files of the networks corresponding to other PIFs
1669 in order to maintain consistency.
1670
1671 params:
1672 pif: Opaque_ref of pif
1673 f : ConfigurationFile(/path/to/ifcfg) to which we append network configuration
1674 """
1675
1676 pifrec = db.get_pif_record(pif)
064af421
BP
1677 nw = pifrec['network']
1678 nwrec = db.get_network_record(nw)
1679 oc = None
1680 bridge = bridge_name(pif)
1681 interface = interface_name(pif)
1682 if bridge:
1683 device = bridge
1684 else:
1685 device = interface
1686
1687 if nwrec.has_key('other_config'):
1688 configure_ethtool(nwrec['other_config'], f)
1689 configure_mtu(nwrec['other_config'], f)
1690 configure_static_routes(device, nwrec['other_config'], f)
1691
1692
1693 if pifrec.has_key('other_config'):
1694 oc = pifrec['other_config']
1695
1696 if device == bridge:
1697 f.write("TYPE=Bridge\n")
1698 f.write("DELAY=0\n")
1699 f.write("STP=off\n")
1700 f.write("PIFDEV=%s\n" % interface_name(pif))
1701
1702 if pifrec['ip_configuration_mode'] == "DHCP":
1703 f.write("BOOTPROTO=dhcp\n")
1704 f.write("PERSISTENT_DHCLIENT=yes\n")
1705 elif pifrec['ip_configuration_mode'] == "Static":
1706 f.write("BOOTPROTO=none\n")
1707 f.write("NETMASK=%(netmask)s\n" % pifrec)
1708 f.write("IPADDR=%(IP)s\n" % pifrec)
1709 f.write("GATEWAY=%(gateway)s\n" % pifrec)
1710 elif pifrec['ip_configuration_mode'] == "None":
1711 f.write("BOOTPROTO=none\n")
1712 else:
1713 raise Error("Unknown ip-configuration-mode %s" % pifrec['ip_configuration_mode'])
1714
1715 if pifrec.has_key('DNS') and pifrec['DNS'] != "":
1716 ServerList = pifrec['DNS'].split(",")
1717 for i in range(len(ServerList)): f.write("DNS%d=%s\n" % (i+1, ServerList[i]))
1718 if oc and oc.has_key('domain'):
1719 f.write("DOMAIN='%s'\n" % oc['domain'].replace(',', ' '))
1720
1721 # We only allow one ifcfg-xenbr* to have PEERDNS=yes and there can be only one GATEWAYDEV in /etc/sysconfig/network.
1722 # The peerdns pif will be the one with pif::other-config:peerdns=true, or the mgmt pif if none have this set.
1723 # The gateway pif will be the one with pif::other-config:defaultroute=true, or the mgmt pif if none have this set.
1724
1725 # Work out which pif on this host should be the one with PEERDNS=yes and which should be the GATEWAYDEV
1726 #
1727 # Note: we prune out the bond master pif (if it exists).
1728 # This is because when we are called to bring up an interface with a bond master, it is implicit that
1729 # we should bring down that master.
1730 pifs_on_host = [ __pif for __pif in db.get_all_pifs() if
c798b21c 1731 not __pif in get_bond_masters_of_pif(pif) ]
064af421
BP
1732 other_pifs_on_host = [ __pif for __pif in pifs_on_host if __pif != pif ]
1733
1734 peerdns_pif = None
1735 defaultroute_pif = None
1736
1737 # loop through all the pifs on this host looking for one with
1738 # other-config:peerdns = true, and one with
1739 # other-config:default-route=true
1740 for __pif in pifs_on_host:
1741 __pifrec = db.get_pif_record(__pif)
1742 __oc = __pifrec['other_config']
1743 if __oc.has_key('peerdns') and __oc['peerdns'] == 'true':
1744 if peerdns_pif == None:
1745 peerdns_pif = __pif
1746 else:
1747 log('Warning: multiple pifs with "peerdns=true" - choosing %s and ignoring %s' % \
1748 (db.get_pif_record(peerdns_pif)['device'], __pifrec['device']))
1749 if __oc.has_key('defaultroute') and __oc['defaultroute'] == 'true':
1750 if defaultroute_pif == None:
1751 defaultroute_pif = __pif
1752 else:
1753 log('Warning: multiple pifs with "defaultroute=true" - choosing %s and ignoring %s' % \
1754 (db.get_pif_record(defaultroute_pif)['device'], __pifrec['device']))
1755
1756 # If no pif is explicitly specified then use the mgmt pif for peerdns/defaultroute
1757 if peerdns_pif == None:
1758 peerdns_pif = management_pif
1759 if defaultroute_pif == None:
1760 defaultroute_pif = management_pif
1761
1762 # Update all the other network's ifcfg files and ensure consistency
1763 for __pif in other_pifs_on_host:
1764 __f = open_network_ifcfg(__pif)
1765 peerdns_line_wanted = 'PEERDNS=%s\n' % ((__pif == peerdns_pif) and 'yes' or 'no')
1766 lines = __f.readlines()
1767
1768 if not peerdns_line_wanted in lines:
1769 # the PIF selected for DNS has changed and as a result this ifcfg file needs rewriting
1770 for line in lines:
1771 if not line.lstrip().startswith('PEERDNS'):
1772 __f.write(line)
1773 log("Setting %s in %s" % (peerdns_line_wanted.strip(), __f.path()))
1774 __f.write(peerdns_line_wanted)
1775 __f.close()
1776 f.attach_child(__f)
1777
1778 else:
1779 # There is no need to change this ifcfg file. So don't attach_child.
1780 pass
1781
1782 # ... and for this pif too
1783 f.write('PEERDNS=%s\n' % ((pif == peerdns_pif) and 'yes' or 'no'))
1784
1785 # Update gatewaydev
1786 fnetwork = ConfigurationFile("network", "/etc/sysconfig")
1787 for line in fnetwork.readlines():
1788 if line.lstrip().startswith('GATEWAY') :
1789 continue
1790 fnetwork.write(line)
1791 if defaultroute_pif:
1792 gatewaydev = bridge_name(defaultroute_pif)
1793 if not gatewaydev:
1794 gatewaydev = interface_name(defaultroute_pif)
1795 fnetwork.write('GATEWAYDEV=%s\n' % gatewaydev)
1796 fnetwork.close()
1797 f.attach_child(fnetwork)
1798
1799 return
1800
1801
1802def configure_physical_interface(pif):
1803 """Write the configuration for a physical interface.
1804
1805 Writes the configuration file for the physical interface described by
1806 the pif object.
1807
1808 Returns the open file handle for the interface configuration file.
1809 """
1810
1811 pifrec = db.get_pif_record(pif)
1812
1813 f = open_pif_ifcfg(pif)
1814
1815 f.write("TYPE=Ethernet\n")
1816 f.write("HWADDR=%(MAC)s\n" % pifrec)
1817
1818 return f
1819
1820def configure_bond_interface(pif):
1821 """Write the configuration for a bond interface.
1822
1823 Writes the configuration file for the bond interface described by
1824 the pif object. Handles writing the configuration for the slave
1825 interfaces.
1826
1827 Returns the open file handle for the bond interface configuration
1828 file.
1829 """
1830
1831 pifrec = db.get_pif_record(pif)
1832 oc = pifrec['other_config']
1833 f = open_pif_ifcfg(pif)
1834
1835 if pifrec['MAC'] != "":
1836 f.write("MACADDR=%s\n" % pifrec['MAC'])
1837
1838 for slave in get_bond_slaves_of_pif(pif):
1839 s = configure_physical_interface(slave)
1840 s.write("MASTER=%(device)s\n" % pifrec)
1841 s.write("SLAVE=yes\n")
1842 s.close()
1843 f.attach_child(s)
1844
1845 # The bond option defaults
1846 bond_options = {
1847 "mode": "balance-slb",
1848 "miimon": "100",
1849 "downdelay": "200",
1850 "updelay": "31000",
1851 "use_carrier": "1",
1852 }
1853
1854 # override defaults with values from other-config whose keys being with "bond-"
1855 overrides = filter(lambda (key,val): key.startswith("bond-"), oc.items())
1856 overrides = map(lambda (key,val): (key[5:], val), overrides)
1857 bond_options.update(overrides)
1858
1859 # write the bond options to ifcfg-bondX
1860 f.write('BONDING_OPTS="')
1861 for (name,val) in bond_options.items():
1862 f.write("%s=%s " % (name,val))
1863 f.write('"\n')
1864 return f
1865
1866def configure_vlan_interface(pif):
1867 """Write the configuration for a VLAN interface.
1868
1869 Writes the configuration file for the VLAN interface described by
1870 the pif object. Handles writing the configuration for the master
1871 interface if necessary.
1872
1873 Returns the open file handle for the VLAN interface configuration
1874 file.
1875 """
1876
1877 slave = configure_pif(get_vlan_slave_of_pif(pif))
1878 slave.close()
1879
1880 f = open_pif_ifcfg(pif)
1881 f.write("VLAN=yes\n")
1882 f.attach_child(slave)
1883
1884 return f
1885
1886def configure_pif(pif):
1887 """Write the configuration for a PIF object.
1888
1889 Writes the configuration file the PIF and all dependent
1890 interfaces (bond slaves and VLAN masters etc).
1891
1892 Returns the open file handle for the interface configuration file.
1893 """
1894
1895 pifrec = db.get_pif_record(pif)
1896
1897 if pifrec['VLAN'] != '-1':
1898 f = configure_vlan_interface(pif)
1899 elif len(pifrec['bond_master_of']) != 0:
1900 f = configure_bond_interface(pif)
1901 else:
1902 f = configure_physical_interface(pif)
1903
1904 bridge = bridge_name(pif)
1905 if bridge:
1906 f.write("BRIDGE=%s\n" % bridge)
1907
1908 return f
1909
1910def unconfigure_pif(pif):
1911 """Clear up the files created by configure_pif"""
1912 f = open_pif_ifcfg(pif)
1913 log("Unlinking stale file %s" % f.path())
1914 f.unlink()
1915 return f
1916\f
1917if __name__ == "__main__":
1918 rc = 1
1919 try:
1920 rc = main()
1921 except:
1922 ex = sys.exc_info()
1923 err = traceback.format_exception(*ex)
1924 for exline in err:
1925 log(exline)
1926
1927 if not debug_mode():
1928 syslog.closelog()
1929
1930 sys.exit(rc)