]> git.proxmox.com Git - ovs.git/blame - xenserver/opt_xensource_libexec_InterfaceReconfigure.py
ovs-dpctl: Fix setting of packet length for "controller" action
[ovs.git] / xenserver / opt_xensource_libexec_InterfaceReconfigure.py
CommitLineData
b3080599
IC
1# Copyright (c) 2008,2009 Citrix Systems, Inc.
2#
3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU Lesser General Public License as published
5# by the Free Software Foundation; version 2.1 only. with the special
6# exception on linking described in file LICENSE.
7#
8# This program is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11# GNU Lesser General Public License for more details.
12#
b63fadcf 13import sys
b3080599
IC
14import syslog
15import os
16
17from xml.dom.minidom import getDOMImplementation
18from xml.dom.minidom import parse as parseXML
19
64ddb6fe
BP
20the_root_prefix = ""
21def root_prefix():
22 """Returns a string to prefix to all file name references, which
23 is useful for testing."""
24 return the_root_prefix
25def set_root_prefix(prefix):
26 global the_root_prefix
27 the_root_prefix = prefix
28
b63fadcf
BP
29log_destination = "syslog"
30def get_log_destination():
31 """Returns the current log destination.
32 'syslog' means "log to syslog".
33 'stderr' means "log to stderr"."""
34 return log_destination
35def set_log_destination(dest):
36 global log_destination
37 log_destination = dest
38
b3080599
IC
39#
40# Logging.
41#
42
43def log(s):
b63fadcf
BP
44 if get_log_destination() == 'syslog':
45 syslog.syslog(s)
46 else:
47 print >>sys.stderr, s
b3080599
IC
48
49#
50# Exceptions.
51#
52
53class Error(Exception):
54 def __init__(self, msg):
55 Exception.__init__(self)
56 self.msg = msg
57
58#
59# Run external utilities
60#
61
62def run_command(command):
63 log("Running command: " + ' '.join(command))
64ddb6fe 64 rc = os.spawnl(os.P_WAIT, root_prefix() + command[0], *command)
b3080599
IC
65 if rc != 0:
66 log("Command failed %d: " % rc + ' '.join(command))
67 return False
68 return True
69
70#
71# Configuration File Handling.
72#
73
74class ConfigurationFile(object):
75 """Write a file, tracking old and new versions.
76
77 Supports writing a new version of a file and applying and
78 reverting those changes.
79 """
80
81 __STATE = {"OPEN":"OPEN",
82 "NOT-APPLIED":"NOT-APPLIED", "APPLIED":"APPLIED",
83 "REVERTED":"REVERTED", "COMMITTED": "COMMITTED"}
84
85 def __init__(self, path):
86 dirname,basename = os.path.split(path)
87
88 self.__state = self.__STATE['OPEN']
89 self.__children = []
90
91 self.__path = os.path.join(dirname, basename)
92 self.__oldpath = os.path.join(dirname, "." + basename + ".xapi-old")
93 self.__newpath = os.path.join(dirname, "." + basename + ".xapi-new")
94
95 self.__f = open(self.__newpath, "w")
96
97 def attach_child(self, child):
98 self.__children.append(child)
99
100 def path(self):
101 return self.__path
102
103 def readlines(self):
104 try:
105 return open(self.path()).readlines()
106 except:
107 return ""
108
109 def write(self, args):
110 if self.__state != self.__STATE['OPEN']:
111 raise Error("Attempt to write to file in state %s" % self.__state)
112 self.__f.write(args)
113
114 def close(self):
115 if self.__state != self.__STATE['OPEN']:
116 raise Error("Attempt to close file in state %s" % self.__state)
117
118 self.__f.close()
119 self.__state = self.__STATE['NOT-APPLIED']
120
121 def changed(self):
122 if self.__state != self.__STATE['NOT-APPLIED']:
123 raise Error("Attempt to compare file in state %s" % self.__state)
124
125 return True
126
127 def apply(self):
128 if self.__state != self.__STATE['NOT-APPLIED']:
129 raise Error("Attempt to apply configuration from state %s" % self.__state)
130
131 for child in self.__children:
132 child.apply()
133
134 log("Applying changes to %s configuration" % self.__path)
135
136 # Remove previous backup.
137 if os.access(self.__oldpath, os.F_OK):
138 os.unlink(self.__oldpath)
139
140 # Save current configuration.
141 if os.access(self.__path, os.F_OK):
142 os.link(self.__path, self.__oldpath)
143 os.unlink(self.__path)
144
145 # Apply new configuration.
146 assert(os.path.exists(self.__newpath))
147 os.link(self.__newpath, self.__path)
148
149 # Remove temporary file.
150 os.unlink(self.__newpath)
151
152 self.__state = self.__STATE['APPLIED']
153
154 def revert(self):
155 if self.__state != self.__STATE['APPLIED']:
156 raise Error("Attempt to revert configuration from state %s" % self.__state)
157
158 for child in self.__children:
159 child.revert()
160
161 log("Reverting changes to %s configuration" % self.__path)
162
163 # Remove existing new configuration
164 if os.access(self.__newpath, os.F_OK):
165 os.unlink(self.__newpath)
166
167 # Revert new configuration.
168 if os.access(self.__path, os.F_OK):
169 os.link(self.__path, self.__newpath)
170 os.unlink(self.__path)
171
172 # Revert to old configuration.
173 if os.access(self.__oldpath, os.F_OK):
174 os.link(self.__oldpath, self.__path)
175 os.unlink(self.__oldpath)
176
177 # Leave .*.xapi-new as an aid to debugging.
178
179 self.__state = self.__STATE['REVERTED']
180
181 def commit(self):
182 if self.__state != self.__STATE['APPLIED']:
183 raise Error("Attempt to commit configuration from state %s" % self.__state)
184
185 for child in self.__children:
186 child.commit()
187
188 log("Committing changes to %s configuration" % self.__path)
189
190 if os.access(self.__oldpath, os.F_OK):
191 os.unlink(self.__oldpath)
192 if os.access(self.__newpath, os.F_OK):
193 os.unlink(self.__newpath)
194
195 self.__state = self.__STATE['COMMITTED']
196
197#
198# Helper functions for encoding/decoding database attributes to/from XML.
199#
200
201def _str_to_xml(xml, parent, tag, val):
202 e = xml.createElement(tag)
203 parent.appendChild(e)
204 v = xml.createTextNode(val)
205 e.appendChild(v)
206def _str_from_xml(n):
207 def getText(nodelist):
208 rc = ""
209 for node in nodelist:
210 if node.nodeType == node.TEXT_NODE:
211 rc = rc + node.data
212 return rc
213 return getText(n.childNodes).strip()
214
215def _bool_to_xml(xml, parent, tag, val):
216 if val:
217 _str_to_xml(xml, parent, tag, "True")
218 else:
219 _str_to_xml(xml, parent, tag, "False")
220def _bool_from_xml(n):
221 s = _str_from_xml(n)
222 if s == "True":
223 return True
224 elif s == "False":
225 return False
226 else:
227 raise Error("Unknown boolean value %s" % s)
228
229def _strlist_to_xml(xml, parent, ltag, itag, val):
230 e = xml.createElement(ltag)
231 parent.appendChild(e)
232 for v in val:
233 c = xml.createElement(itag)
234 e.appendChild(c)
235 cv = xml.createTextNode(v)
236 c.appendChild(cv)
237def _strlist_from_xml(n, ltag, itag):
238 ret = []
239 for n in n.childNodes:
240 if n.nodeName == itag:
241 ret.append(_str_from_xml(n))
242 return ret
243
244def _otherconfig_to_xml(xml, parent, val, attrs):
245 otherconfig = xml.createElement("other_config")
246 parent.appendChild(otherconfig)
247 for n,v in val.items():
248 if not n in attrs:
249 raise Error("Unknown other-config attribute: %s" % n)
250 _str_to_xml(xml, otherconfig, n, v)
251def _otherconfig_from_xml(n, attrs):
252 ret = {}
253 for n in n.childNodes:
254 if n.nodeName in attrs:
255 ret[n.nodeName] = _str_from_xml(n)
256 return ret
257
258#
259# Definitions of the database objects (and their attributes) used by interface-reconfigure.
260#
261# Each object is defined by a dictionary mapping an attribute name in
262# the xapi database to a tuple containing two items:
263# - a function which takes this attribute and encodes it as XML.
264# - a function which takes XML and decocdes it into a value.
265#
266# other-config attributes are specified as a simple array of strings
267
268_PIF_XML_TAG = "pif"
269_VLAN_XML_TAG = "vlan"
270_BOND_XML_TAG = "bond"
271_NETWORK_XML_TAG = "network"
272
273_ETHTOOL_OTHERCONFIG_ATTRS = ['ethtool-%s' % x for x in 'autoneg', 'speed', 'duplex', 'rx', 'tx', 'sg', 'tso', 'ufo', 'gso' ]
274
275_PIF_OTHERCONFIG_ATTRS = [ 'domain', 'peerdns', 'defaultroute', 'mtu', 'static-routes' ] + \
276 [ 'bond-%s' % x for x in 'mode', 'miimon', 'downdelay', 'updelay', 'use_carrier' ] + \
277 _ETHTOOL_OTHERCONFIG_ATTRS
278
279_PIF_ATTRS = { 'uuid': (_str_to_xml,_str_from_xml),
280 'management': (_bool_to_xml,_bool_from_xml),
281 'network': (_str_to_xml,_str_from_xml),
282 'device': (_str_to_xml,_str_from_xml),
283 'bond_master_of': (lambda x, p, t, v: _strlist_to_xml(x, p, 'bond_master_of', 'slave', v),
284 lambda n: _strlist_from_xml(n, 'bond_master_of', 'slave')),
285 'bond_slave_of': (_str_to_xml,_str_from_xml),
286 'VLAN': (_str_to_xml,_str_from_xml),
287 'VLAN_master_of': (_str_to_xml,_str_from_xml),
288 'VLAN_slave_of': (lambda x, p, t, v: _strlist_to_xml(x, p, 'VLAN_slave_of', 'master', v),
289 lambda n: _strlist_from_xml(n, 'VLAN_slave_Of', 'master')),
290 'ip_configuration_mode': (_str_to_xml,_str_from_xml),
291 'IP': (_str_to_xml,_str_from_xml),
292 'netmask': (_str_to_xml,_str_from_xml),
293 'gateway': (_str_to_xml,_str_from_xml),
294 'DNS': (_str_to_xml,_str_from_xml),
295 'MAC': (_str_to_xml,_str_from_xml),
296 'other_config': (lambda x, p, t, v: _otherconfig_to_xml(x, p, v, _PIF_OTHERCONFIG_ATTRS),
297 lambda n: _otherconfig_from_xml(n, _PIF_OTHERCONFIG_ATTRS)),
298
299 # Special case: We write the current value
300 # PIF.currently-attached to the cache but since it will
301 # not be valid when we come to use the cache later
302 # (i.e. after a reboot) we always read it as False.
303 'currently_attached': (_bool_to_xml, lambda n: False),
304 }
305
306_VLAN_ATTRS = { 'uuid': (_str_to_xml,_str_from_xml),
307 'tagged_PIF': (_str_to_xml,_str_from_xml),
308 'untagged_PIF': (_str_to_xml,_str_from_xml),
309 }
310
311_BOND_ATTRS = { 'uuid': (_str_to_xml,_str_from_xml),
312 'master': (_str_to_xml,_str_from_xml),
313 'slaves': (lambda x, p, t, v: _strlist_to_xml(x, p, 'slaves', 'slave', v),
314 lambda n: _strlist_from_xml(n, 'slaves', 'slave')),
315 }
316
317_NETWORK_OTHERCONFIG_ATTRS = [ 'mtu', 'static-routes' ] + _ETHTOOL_OTHERCONFIG_ATTRS
318
319_NETWORK_ATTRS = { 'uuid': (_str_to_xml,_str_from_xml),
320 'bridge': (_str_to_xml,_str_from_xml),
321 'PIFs': (lambda x, p, t, v: _strlist_to_xml(x, p, 'PIFs', 'PIF', v),
322 lambda n: _strlist_from_xml(n, 'PIFs', 'PIF')),
323 'other_config': (lambda x, p, t, v: _otherconfig_to_xml(x, p, v, _NETWORK_OTHERCONFIG_ATTRS),
324 lambda n: _otherconfig_from_xml(n, _NETWORK_OTHERCONFIG_ATTRS)),
325 }
326
327#
328# Database Cache object
329#
330
331_db = None
332
333def db():
334 assert(_db is not None)
335 return _db
336
337def db_init_from_cache(cache):
338 global _db
339 assert(_db is None)
340 _db = DatabaseCache(cache_file=cache)
341
342def db_init_from_xenapi(session):
343 global _db
344 assert(_db is None)
345 _db = DatabaseCache(session_ref=session)
346
347class DatabaseCache(object):
348 def __read_xensource_inventory(self):
64ddb6fe 349 filename = root_prefix() + "/etc/xensource-inventory"
b3080599
IC
350 f = open(filename, "r")
351 lines = [x.strip("\n") for x in f.readlines()]
352 f.close()
353
354 defs = [ (l[:l.find("=")], l[(l.find("=") + 1):]) for l in lines ]
355 defs = [ (a, b.strip("'")) for (a,b) in defs ]
356
357 return dict(defs)
13ffee26 358
b3080599
IC
359 def __pif_on_host(self,pif):
360 return self.__pifs.has_key(pif)
361
362 def __get_pif_records_from_xapi(self, session, host):
363 self.__pifs = {}
364 for (p,rec) in session.xenapi.PIF.get_all_records().items():
365 if rec['host'] != host:
366 continue
367 self.__pifs[p] = {}
368 for f in _PIF_ATTRS:
369 self.__pifs[p][f] = rec[f]
370 self.__pifs[p]['other_config'] = {}
371 for f in _PIF_OTHERCONFIG_ATTRS:
372 if not rec['other_config'].has_key(f): continue
373 self.__pifs[p]['other_config'][f] = rec['other_config'][f]
374
375 def __get_vlan_records_from_xapi(self, session):
376 self.__vlans = {}
377 for v in session.xenapi.VLAN.get_all():
378 rec = session.xenapi.VLAN.get_record(v)
379 if not self.__pif_on_host(rec['untagged_PIF']):
380 continue
381 self.__vlans[v] = {}
382 for f in _VLAN_ATTRS:
383 self.__vlans[v][f] = rec[f]
384
385 def __get_bond_records_from_xapi(self, session):
386 self.__bonds = {}
387 for b in session.xenapi.Bond.get_all():
388 rec = session.xenapi.Bond.get_record(b)
389 if not self.__pif_on_host(rec['master']):
390 continue
391 self.__bonds[b] = {}
392 for f in _BOND_ATTRS:
393 self.__bonds[b][f] = rec[f]
394
395 def __get_network_records_from_xapi(self, session):
396 self.__networks = {}
397 for n in session.xenapi.network.get_all():
398 rec = session.xenapi.network.get_record(n)
399 self.__networks[n] = {}
400 for f in _NETWORK_ATTRS:
401 if f == "PIFs":
402 # drop PIFs on other hosts
403 self.__networks[n][f] = [p for p in rec[f] if self.__pif_on_host(p)]
404 else:
405 self.__networks[n][f] = rec[f]
406 self.__networks[n]['other_config'] = {}
407 for f in _NETWORK_OTHERCONFIG_ATTRS:
408 if not rec['other_config'].has_key(f): continue
409 self.__networks[n]['other_config'][f] = rec['other_config'][f]
410
411 def __to_xml(self, xml, parent, key, ref, rec, attrs):
412 """Encode a database object as XML"""
413 e = xml.createElement(key)
414 parent.appendChild(e)
415 if ref:
416 e.setAttribute('ref', ref)
417
418 for n,v in rec.items():
419 if attrs.has_key(n):
420 h,_ = attrs[n]
421 h(xml, e, n, v)
422 else:
423 raise Error("Unknown attribute %s" % n)
424 def __from_xml(self, e, attrs):
425 """Decode a database object from XML"""
426 ref = e.attributes['ref'].value
427 rec = {}
428 for n in e.childNodes:
429 if n.nodeName in attrs:
430 _,h = attrs[n.nodeName]
431 rec[n.nodeName] = h(n)
432 return (ref,rec)
433
434 def __init__(self, session_ref=None, cache_file=None):
435 if session_ref and cache_file:
436 raise Error("can't specify session reference and cache file")
437 if cache_file == None:
438 import XenAPI
439 session = XenAPI.xapi_local()
440
441 if not session_ref:
442 log("No session ref given on command line, logging in.")
443 session.xenapi.login_with_password("root", "")
444 else:
445 session._session = session_ref
446
447 try:
448
449 inventory = self.__read_xensource_inventory()
450 assert(inventory.has_key('INSTALLATION_UUID'))
451 log("host uuid is %s" % inventory['INSTALLATION_UUID'])
452
453 host = session.xenapi.host.get_by_uuid(inventory['INSTALLATION_UUID'])
454
455 self.__get_pif_records_from_xapi(session, host)
456
457 self.__get_vlan_records_from_xapi(session)
458 self.__get_bond_records_from_xapi(session)
459 self.__get_network_records_from_xapi(session)
460 finally:
461 if not session_ref:
462 session.xenapi.session.logout()
463 else:
464 log("Loading xapi database cache from %s" % cache_file)
465
64ddb6fe 466 xml = parseXML(root_prefix() + cache_file)
b3080599
IC
467
468 self.__pifs = {}
469 self.__bonds = {}
470 self.__vlans = {}
471 self.__networks = {}
472
473 assert(len(xml.childNodes) == 1)
474 toplevel = xml.childNodes[0]
475
476 assert(toplevel.nodeName == "xenserver-network-configuration")
477
478 for n in toplevel.childNodes:
479 if n.nodeName == "#text":
480 pass
481 elif n.nodeName == _PIF_XML_TAG:
482 (ref,rec) = self.__from_xml(n, _PIF_ATTRS)
483 self.__pifs[ref] = rec
484 elif n.nodeName == _BOND_XML_TAG:
485 (ref,rec) = self.__from_xml(n, _BOND_ATTRS)
486 self.__bonds[ref] = rec
487 elif n.nodeName == _VLAN_XML_TAG:
488 (ref,rec) = self.__from_xml(n, _VLAN_ATTRS)
489 self.__vlans[ref] = rec
490 elif n.nodeName == _NETWORK_XML_TAG:
491 (ref,rec) = self.__from_xml(n, _NETWORK_ATTRS)
492 self.__networks[ref] = rec
493 else:
494 raise Error("Unknown XML element %s" % n.nodeName)
495
496 def save(self, cache_file):
497
498 xml = getDOMImplementation().createDocument(
499 None, "xenserver-network-configuration", None)
500 for (ref,rec) in self.__pifs.items():
501 self.__to_xml(xml, xml.documentElement, _PIF_XML_TAG, ref, rec, _PIF_ATTRS)
502 for (ref,rec) in self.__bonds.items():
503 self.__to_xml(xml, xml.documentElement, _BOND_XML_TAG, ref, rec, _BOND_ATTRS)
504 for (ref,rec) in self.__vlans.items():
505 self.__to_xml(xml, xml.documentElement, _VLAN_XML_TAG, ref, rec, _VLAN_ATTRS)
506 for (ref,rec) in self.__networks.items():
507 self.__to_xml(xml, xml.documentElement, _NETWORK_XML_TAG, ref, rec,
508 _NETWORK_ATTRS)
509
510 f = open(cache_file, 'w')
511 f.write(xml.toprettyxml())
512 f.close()
513
514 def get_pif_by_uuid(self, uuid):
515 pifs = map(lambda (ref,rec): ref,
516 filter(lambda (ref,rec): uuid == rec['uuid'],
517 self.__pifs.items()))
518 if len(pifs) == 0:
519 raise Error("Unknown PIF \"%s\"" % uuid)
520 elif len(pifs) > 1:
521 raise Error("Non-unique PIF \"%s\"" % uuid)
522
523 return pifs[0]
524
525 def get_pifs_by_device(self, device):
526 return map(lambda (ref,rec): ref,
527 filter(lambda (ref,rec): rec['device'] == device,
528 self.__pifs.items()))
529
530 def get_pif_by_bridge(self, bridge):
531 networks = map(lambda (ref,rec): ref,
532 filter(lambda (ref,rec): rec['bridge'] == bridge,
533 self.__networks.items()))
534 if len(networks) == 0:
535 raise Error("No matching network \"%s\"" % bridge)
536
537 answer = None
538 for network in networks:
539 nwrec = self.get_network_record(network)
540 for pif in nwrec['PIFs']:
541 pifrec = self.get_pif_record(pif)
542 if answer:
543 raise Error("Multiple PIFs on host for network %s" % (bridge))
544 answer = pif
545 if not answer:
546 raise Error("No PIF on host for network %s" % (bridge))
547 return answer
548
549 def get_pif_record(self, pif):
550 if self.__pifs.has_key(pif):
551 return self.__pifs[pif]
552 raise Error("Unknown PIF \"%s\"" % pif)
553 def get_all_pifs(self):
554 return self.__pifs
555 def pif_exists(self, pif):
556 return self.__pifs.has_key(pif)
557
558 def get_management_pif(self):
559 """ Returns the management pif on host
560 """
561 all = self.get_all_pifs()
562 for pif in all:
563 pifrec = self.get_pif_record(pif)
564 if pifrec['management']: return pif
565 return None
566
567 def get_network_record(self, network):
568 if self.__networks.has_key(network):
569 return self.__networks[network]
570 raise Error("Unknown network \"%s\"" % network)
571
572 def get_bond_record(self, bond):
573 if self.__bonds.has_key(bond):
574 return self.__bonds[bond]
575 else:
576 return None
577
578 def get_vlan_record(self, vlan):
579 if self.__vlans.has_key(vlan):
580 return self.__vlans[vlan]
581 else:
582 return None
583
584#
585#
586#
587
588def ethtool_settings(oc):
589 settings = []
590 if oc.has_key('ethtool-speed'):
591 val = oc['ethtool-speed']
592 if val in ["10", "100", "1000"]:
593 settings += ['speed', val]
594 else:
595 log("Invalid value for ethtool-speed = %s. Must be 10|100|1000." % val)
596 if oc.has_key('ethtool-duplex'):
597 val = oc['ethtool-duplex']
598 if val in ["10", "100", "1000"]:
599 settings += ['duplex', 'val']
600 else:
601 log("Invalid value for ethtool-duplex = %s. Must be half|full." % val)
602 if oc.has_key('ethtool-autoneg'):
603 val = oc['ethtool-autoneg']
604 if val in ["true", "on"]:
605 settings += ['autoneg', 'on']
606 elif val in ["false", "off"]:
607 settings += ['autoneg', 'off']
608 else:
609 log("Invalid value for ethtool-autoneg = %s. Must be on|true|off|false." % val)
610 offload = []
611 for opt in ("rx", "tx", "sg", "tso", "ufo", "gso"):
612 if oc.has_key("ethtool-" + opt):
613 val = oc["ethtool-" + opt]
614 if val in ["true", "on"]:
615 offload += [opt, 'on']
616 elif val in ["false", "off"]:
617 offload += [opt, 'off']
618 else:
619 log("Invalid value for ethtool-%s = %s. Must be on|true|off|false." % (opt, val))
620 return settings,offload
621
622def mtu_setting(oc):
623 if oc.has_key('mtu'):
624 try:
625 int(oc['mtu']) # Check that the value is an integer
626 return oc['mtu']
627 except ValueError, x:
628 log("Invalid value for mtu = %s" % oc['mtu'])
629 return None
630
631#
632# IP Network Devices -- network devices with IP configuration
633#
634def pif_ipdev_name(pif):
635 """Return the ipdev name associated with pif"""
636 pifrec = db().get_pif_record(pif)
637 nwrec = db().get_network_record(pifrec['network'])
638
639 if nwrec['bridge']:
640 # TODO: sanity check that nwrec['bridgeless'] != 'true'
641 return nwrec['bridge']
642 else:
643 # TODO: sanity check that nwrec['bridgeless'] == 'true'
644 return pif_netdev_name(pif)
645
646#
647# Bare Network Devices -- network devices without IP configuration
648#
649
650def netdev_exists(netdev):
64ddb6fe 651 return os.path.exists(root_prefix() + "/sys/class/net/" + netdev)
b3080599
IC
652
653def pif_netdev_name(pif):
654 """Get the netdev name for a PIF."""
655
656 pifrec = db().get_pif_record(pif)
657
658 if pif_is_vlan(pif):
659 return "%(device)s.%(VLAN)s" % pifrec
660 else:
661 return pifrec['device']
662
96c7918c
BP
663#
664# Bridges
665#
666
667def pif_is_bridged(pif):
668 pifrec = db().get_pif_record(pif)
669 nwrec = db().get_network_record(pifrec['network'])
670
671 if nwrec['bridge']:
672 # TODO: sanity check that nwrec['bridgeless'] != 'true'
673 return True
674 else:
675 # TODO: sanity check that nwrec['bridgeless'] == 'true'
676 return False
677
678def pif_bridge_name(pif):
679 """Return the bridge name of a pif.
680
681 PIF must be a bridged PIF."""
682 pifrec = db().get_pif_record(pif)
683
684 nwrec = db().get_network_record(pifrec['network'])
685
686 if nwrec['bridge']:
687 return nwrec['bridge']
688 else:
689 raise Error("PIF %(uuid)s does not have a bridge name" % pifrec)
690
b3080599
IC
691#
692# Bonded PIFs
693#
694def pif_is_bond(pif):
695 pifrec = db().get_pif_record(pif)
696
697 return len(pifrec['bond_master_of']) > 0
698
699def pif_get_bond_masters(pif):
700 """Returns a list of PIFs which are bond masters of this PIF"""
701
702 pifrec = db().get_pif_record(pif)
703
704 bso = pifrec['bond_slave_of']
705
706 # bond-slave-of is currently a single reference but in principle a
707 # PIF could be a member of several bonds which are not
708 # concurrently attached. Be robust to this possibility.
709 if not bso or bso == "OpaqueRef:NULL":
710 bso = []
711 elif not type(bso) == list:
712 bso = [bso]
713
714 bondrecs = [db().get_bond_record(bond) for bond in bso]
715 bondrecs = [rec for rec in bondrecs if rec]
716
717 return [bond['master'] for bond in bondrecs]
718
719def pif_get_bond_slaves(pif):
720 """Returns a list of PIFs which make up the given bonded pif."""
721
722 pifrec = db().get_pif_record(pif)
723
724 bmo = pifrec['bond_master_of']
725 if len(bmo) > 1:
726 raise Error("Bond-master-of contains too many elements")
727
728 if len(bmo) == 0:
729 return []
730
731 bondrec = db().get_bond_record(bmo[0])
732 if not bondrec:
733 raise Error("No bond record for bond master PIF")
734
735 return bondrec['slaves']
736
737#
738# VLAN PIFs
739#
740
741def pif_is_vlan(pif):
742 return db().get_pif_record(pif)['VLAN'] != '-1'
743
744def pif_get_vlan_slave(pif):
745 """Find the PIF which is the VLAN slave of pif.
746
747Returns the 'physical' PIF underneath the a VLAN PIF @pif."""
748
749 pifrec = db().get_pif_record(pif)
750
751 vlan = pifrec['VLAN_master_of']
752 if not vlan or vlan == "OpaqueRef:NULL":
753 raise Error("PIF is not a VLAN master")
754
755 vlanrec = db().get_vlan_record(vlan)
756 if not vlanrec:
757 raise Error("No VLAN record found for PIF")
758
759 return vlanrec['tagged_PIF']
760
761def pif_get_vlan_masters(pif):
762 """Returns a list of PIFs which are VLANs on top of the given pif."""
763
764 pifrec = db().get_pif_record(pif)
765 vlans = [db().get_vlan_record(v) for v in pifrec['VLAN_slave_of']]
766 return [v['untagged_PIF'] for v in vlans if v and db().pif_exists(v['untagged_PIF'])]
767
768#
769# Datapath base class
770#
771
772class Datapath(object):
773 """Object encapsulating the actions necessary to (de)configure the
774 datapath for a given PIF. Does not include configuration of the
775 IP address on the ipdev.
776 """
777
778 def __init__(self, pif):
779 self._pif = pif
780
781 def configure_ipdev(self, cfg):
782 """Write ifcfg TYPE field for an IPdev, plus any type specific
783 fields to cfg
784 """
785 raise NotImplementedError
786
787 def preconfigure(self, parent):
788 """Prepare datapath configuration for PIF, but do not actually
789 apply any changes.
790
791 Any configuration files should be attached to parent.
792 """
793 raise NotImplementedError
794
795 def bring_down_existing(self):
796 """Tear down any existing network device configuration which
797 needs to be undone in order to bring this PIF up.
798 """
799 raise NotImplementedError
800
801 def configure(self):
802 """Apply the configuration prepared in the preconfigure stage.
803
804 Should assume any configuration files changed attached in
805 the preconfigure stage are applied and bring up the
806 necesary devices to provide the datapath for the
807 PIF.
808
809 Should not bring up the IPdev.
810 """
811 raise NotImplementedError
812
813 def post(self):
814 """Called after the IPdev has been brought up.
815
816 Should do any final setup, including reinstating any
817 devices which were taken down in the bring_down_existing
818 hook.
819 """
820 raise NotImplementedError
821
822 def bring_down(self):
823 """Tear down and deconfigure the datapath. Should assume the
824 IPdev has already been brought down.
825 """
826 raise NotImplementedError
827
828def DatapathFactory(pif):
829 # XXX Need a datapath object for bridgeless PIFs
830
831 try:
64ddb6fe 832 network_conf = open(root_prefix() + "/etc/xensource/network.conf", 'r')
b3080599
IC
833 network_backend = network_conf.readline().strip()
834 network_conf.close()
835 except Exception, e:
836 raise Error("failed to determine network backend:" + e)
837
838 if network_backend == "bridge":
839 from InterfaceReconfigureBridge import DatapathBridge
840 return DatapathBridge(pif)
841 elif network_backend == "vswitch":
842 from InterfaceReconfigureVswitch import DatapathVswitch
843 return DatapathVswitch(pif)
844 else:
845 raise Error("unknown network backend %s" % network_backend)