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