1 # Copyright (c) 2008,2009 Citrix Systems, Inc.
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.
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.
17 from xml.dom.minidom import getDOMImplementation
18 from xml.dom.minidom import parse as parseXML
22 """Returns a string to prefix to all file name references, which
23 is useful for testing."""
24 return the_root_prefix
25 def set_root_prefix(prefix):
26 global the_root_prefix
27 the_root_prefix = prefix
29 log_destination = "syslog"
30 def 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
35 def set_log_destination(dest):
36 global log_destination
37 log_destination = dest
44 if get_log_destination() == 'syslog':
53 class Error(Exception):
54 def __init__(self, msg):
55 Exception.__init__(self)
59 # Run external utilities
62 def run_command(command):
63 log("Running command: " + ' '.join(command))
64 rc = os.spawnl(os.P_WAIT, root_prefix() + command[0], *command)
66 log("Command failed %d: " % rc + ' '.join(command))
71 # Configuration File Handling.
74 class ConfigurationFile(object):
75 """Write a file, tracking old and new versions.
77 Supports writing a new version of a file and applying and
78 reverting those changes.
81 __STATE = {"OPEN":"OPEN",
82 "NOT-APPLIED":"NOT-APPLIED", "APPLIED":"APPLIED",
83 "REVERTED":"REVERTED", "COMMITTED": "COMMITTED"}
85 def __init__(self, path):
86 dirname,basename = os.path.split(path)
88 self.__state = self.__STATE['OPEN']
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")
95 self.__f = open(self.__newpath, "w")
97 def attach_child(self, child):
98 self.__children.append(child)
105 return open(self.path()).readlines()
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)
115 if self.__state != self.__STATE['OPEN']:
116 raise Error("Attempt to close file in state %s" % self.__state)
119 self.__state = self.__STATE['NOT-APPLIED']
122 if self.__state != self.__STATE['NOT-APPLIED']:
123 raise Error("Attempt to compare file in state %s" % self.__state)
128 if self.__state != self.__STATE['NOT-APPLIED']:
129 raise Error("Attempt to apply configuration from state %s" % self.__state)
131 for child in self.__children:
134 log("Applying changes to %s configuration" % self.__path)
136 # Remove previous backup.
137 if os.access(self.__oldpath, os.F_OK):
138 os.unlink(self.__oldpath)
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)
145 # Apply new configuration.
146 assert(os.path.exists(self.__newpath))
147 os.link(self.__newpath, self.__path)
149 # Remove temporary file.
150 os.unlink(self.__newpath)
152 self.__state = self.__STATE['APPLIED']
155 if self.__state != self.__STATE['APPLIED']:
156 raise Error("Attempt to revert configuration from state %s" % self.__state)
158 for child in self.__children:
161 log("Reverting changes to %s configuration" % self.__path)
163 # Remove existing new configuration
164 if os.access(self.__newpath, os.F_OK):
165 os.unlink(self.__newpath)
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)
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)
177 # Leave .*.xapi-new as an aid to debugging.
179 self.__state = self.__STATE['REVERTED']
182 if self.__state != self.__STATE['APPLIED']:
183 raise Error("Attempt to commit configuration from state %s" % self.__state)
185 for child in self.__children:
188 log("Committing changes to %s configuration" % self.__path)
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)
195 self.__state = self.__STATE['COMMITTED']
198 # Helper functions for encoding/decoding database attributes to/from XML.
201 def _str_to_xml(xml, parent, tag, val):
202 e = xml.createElement(tag)
203 parent.appendChild(e)
204 v = xml.createTextNode(val)
206 def _str_from_xml(n):
207 def getText(nodelist):
209 for node in nodelist:
210 if node.nodeType == node.TEXT_NODE:
213 return getText(n.childNodes).strip()
215 def _bool_to_xml(xml, parent, tag, val):
217 _str_to_xml(xml, parent, tag, "True")
219 _str_to_xml(xml, parent, tag, "False")
220 def _bool_from_xml(n):
227 raise Error("Unknown boolean value %s" % s)
229 def _strlist_to_xml(xml, parent, ltag, itag, val):
230 e = xml.createElement(ltag)
231 parent.appendChild(e)
233 c = xml.createElement(itag)
235 cv = xml.createTextNode(v)
237 def _strlist_from_xml(n, ltag, itag):
239 for n in n.childNodes:
240 if n.nodeName == itag:
241 ret.append(_str_from_xml(n))
244 def _otherconfig_to_xml(xml, parent, val, attrs):
245 otherconfig = xml.createElement("other_config")
246 parent.appendChild(otherconfig)
247 for n,v in val.items():
249 raise Error("Unknown other-config attribute: %s" % n)
250 _str_to_xml(xml, otherconfig, n, v)
251 def _otherconfig_from_xml(n, attrs):
253 for n in n.childNodes:
254 if n.nodeName in attrs:
255 ret[n.nodeName] = _str_from_xml(n)
259 # Definitions of the database objects (and their attributes) used by interface-reconfigure.
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.
266 # other-config attributes are specified as a simple array of strings
269 _VLAN_XML_TAG = "vlan"
270 _BOND_XML_TAG = "bond"
271 _NETWORK_XML_TAG = "network"
273 _ETHTOOL_OTHERCONFIG_ATTRS = ['ethtool-%s' % x for x in 'autoneg', 'speed', 'duplex', 'rx', 'tx', 'sg', 'tso', 'ufo', 'gso' ]
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
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)),
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),
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),
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')),
317 _NETWORK_OTHERCONFIG_ATTRS = [ 'mtu', 'static-routes' ] + _ETHTOOL_OTHERCONFIG_ATTRS
319 _NETWORK_ATTRS = { 'uuid': (_str_to_xml,_str_from_xml),
320 'bridge': (_str_to_xml,_str_from_xml),
321 'MTU': (_str_to_xml,_str_from_xml),
322 'PIFs': (lambda x, p, t, v: _strlist_to_xml(x, p, 'PIFs', 'PIF', v),
323 lambda n: _strlist_from_xml(n, 'PIFs', 'PIF')),
324 'other_config': (lambda x, p, t, v: _otherconfig_to_xml(x, p, v, _NETWORK_OTHERCONFIG_ATTRS),
325 lambda n: _otherconfig_from_xml(n, _NETWORK_OTHERCONFIG_ATTRS)),
329 # Database Cache object
335 assert(_db is not None)
338 def db_init_from_cache(cache):
341 _db = DatabaseCache(cache_file=cache)
343 def db_init_from_xenapi(session):
346 _db = DatabaseCache(session_ref=session)
348 class DatabaseCache(object):
349 def __read_xensource_inventory(self):
350 filename = root_prefix() + "/etc/xensource-inventory"
351 f = open(filename, "r")
352 lines = [x.strip("\n") for x in f.readlines()]
355 defs = [ (l[:l.find("=")], l[(l.find("=") + 1):]) for l in lines ]
356 defs = [ (a, b.strip("'")) for (a,b) in defs ]
360 def __pif_on_host(self,pif):
361 return self.__pifs.has_key(pif)
363 def __get_pif_records_from_xapi(self, session, host):
365 for (p,rec) in session.xenapi.PIF.get_all_records().items():
366 if rec['host'] != host:
370 self.__pifs[p][f] = rec[f]
371 self.__pifs[p]['other_config'] = {}
372 for f in _PIF_OTHERCONFIG_ATTRS:
373 if not rec['other_config'].has_key(f): continue
374 self.__pifs[p]['other_config'][f] = rec['other_config'][f]
376 def __get_vlan_records_from_xapi(self, session):
378 for v in session.xenapi.VLAN.get_all():
379 rec = session.xenapi.VLAN.get_record(v)
380 if not self.__pif_on_host(rec['untagged_PIF']):
383 for f in _VLAN_ATTRS:
384 self.__vlans[v][f] = rec[f]
386 def __get_bond_records_from_xapi(self, session):
388 for b in session.xenapi.Bond.get_all():
389 rec = session.xenapi.Bond.get_record(b)
390 if not self.__pif_on_host(rec['master']):
393 for f in _BOND_ATTRS:
394 self.__bonds[b][f] = rec[f]
396 def __get_network_records_from_xapi(self, session):
398 for n in session.xenapi.network.get_all():
399 rec = session.xenapi.network.get_record(n)
400 self.__networks[n] = {}
401 for f in _NETWORK_ATTRS:
403 # drop PIFs on other hosts
404 self.__networks[n][f] = [p for p in rec[f] if self.__pif_on_host(p)]
405 elif f == "MTU" and f not in rec:
406 # XenServer 5.5 network records did not have an
407 # MTU field, so allow this to be missing.
410 self.__networks[n][f] = rec[f]
411 self.__networks[n]['other_config'] = {}
412 for f in _NETWORK_OTHERCONFIG_ATTRS:
413 if not rec['other_config'].has_key(f): continue
414 self.__networks[n]['other_config'][f] = rec['other_config'][f]
416 def __to_xml(self, xml, parent, key, ref, rec, attrs):
417 """Encode a database object as XML"""
418 e = xml.createElement(key)
419 parent.appendChild(e)
421 e.setAttribute('ref', ref)
423 for n,v in rec.items():
428 raise Error("Unknown attribute %s" % n)
429 def __from_xml(self, e, attrs):
430 """Decode a database object from XML"""
431 ref = e.attributes['ref'].value
433 for n in e.childNodes:
434 if n.nodeName in attrs:
435 _,h = attrs[n.nodeName]
436 rec[n.nodeName] = h(n)
439 def __init__(self, session_ref=None, cache_file=None):
440 if session_ref and cache_file:
441 raise Error("can't specify session reference and cache file")
442 if cache_file == None:
444 session = XenAPI.xapi_local()
447 log("No session ref given on command line, logging in.")
448 session.xenapi.login_with_password("root", "")
450 session._session = session_ref
454 inventory = self.__read_xensource_inventory()
455 assert(inventory.has_key('INSTALLATION_UUID'))
456 log("host uuid is %s" % inventory['INSTALLATION_UUID'])
458 host = session.xenapi.host.get_by_uuid(inventory['INSTALLATION_UUID'])
460 self.__get_pif_records_from_xapi(session, host)
462 self.__get_vlan_records_from_xapi(session)
463 self.__get_bond_records_from_xapi(session)
464 self.__get_network_records_from_xapi(session)
467 session.xenapi.session.logout()
469 log("Loading xapi database cache from %s" % cache_file)
471 xml = parseXML(root_prefix() + cache_file)
478 assert(len(xml.childNodes) == 1)
479 toplevel = xml.childNodes[0]
481 assert(toplevel.nodeName == "xenserver-network-configuration")
483 for n in toplevel.childNodes:
484 if n.nodeName == "#text":
486 elif n.nodeName == _PIF_XML_TAG:
487 (ref,rec) = self.__from_xml(n, _PIF_ATTRS)
488 self.__pifs[ref] = rec
489 elif n.nodeName == _BOND_XML_TAG:
490 (ref,rec) = self.__from_xml(n, _BOND_ATTRS)
491 self.__bonds[ref] = rec
492 elif n.nodeName == _VLAN_XML_TAG:
493 (ref,rec) = self.__from_xml(n, _VLAN_ATTRS)
494 self.__vlans[ref] = rec
495 elif n.nodeName == _NETWORK_XML_TAG:
496 (ref,rec) = self.__from_xml(n, _NETWORK_ATTRS)
497 self.__networks[ref] = rec
499 raise Error("Unknown XML element %s" % n.nodeName)
501 def save(self, cache_file):
503 xml = getDOMImplementation().createDocument(
504 None, "xenserver-network-configuration", None)
505 for (ref,rec) in self.__pifs.items():
506 self.__to_xml(xml, xml.documentElement, _PIF_XML_TAG, ref, rec, _PIF_ATTRS)
507 for (ref,rec) in self.__bonds.items():
508 self.__to_xml(xml, xml.documentElement, _BOND_XML_TAG, ref, rec, _BOND_ATTRS)
509 for (ref,rec) in self.__vlans.items():
510 self.__to_xml(xml, xml.documentElement, _VLAN_XML_TAG, ref, rec, _VLAN_ATTRS)
511 for (ref,rec) in self.__networks.items():
512 self.__to_xml(xml, xml.documentElement, _NETWORK_XML_TAG, ref, rec,
515 f = open(cache_file, 'w')
516 f.write(xml.toprettyxml())
519 def get_pif_by_uuid(self, uuid):
520 pifs = map(lambda (ref,rec): ref,
521 filter(lambda (ref,rec): uuid == rec['uuid'],
522 self.__pifs.items()))
524 raise Error("Unknown PIF \"%s\"" % uuid)
526 raise Error("Non-unique PIF \"%s\"" % uuid)
530 def get_pifs_by_device(self, device):
531 return map(lambda (ref,rec): ref,
532 filter(lambda (ref,rec): rec['device'] == device,
533 self.__pifs.items()))
535 def get_pif_by_bridge(self, bridge):
536 networks = map(lambda (ref,rec): ref,
537 filter(lambda (ref,rec): rec['bridge'] == bridge,
538 self.__networks.items()))
539 if len(networks) == 0:
540 raise Error("No matching network \"%s\"" % bridge)
543 for network in networks:
544 nwrec = self.get_network_record(network)
545 for pif in nwrec['PIFs']:
546 pifrec = self.get_pif_record(pif)
548 raise Error("Multiple PIFs on host for network %s" % (bridge))
551 raise Error("No PIF on host for network %s" % (bridge))
554 def get_pif_record(self, pif):
555 if self.__pifs.has_key(pif):
556 return self.__pifs[pif]
557 raise Error("Unknown PIF \"%s\"" % pif)
558 def get_all_pifs(self):
560 def pif_exists(self, pif):
561 return self.__pifs.has_key(pif)
563 def get_management_pif(self):
564 """ Returns the management pif on host
566 all = self.get_all_pifs()
568 pifrec = self.get_pif_record(pif)
569 if pifrec['management']: return pif
572 def get_network_record(self, network):
573 if self.__networks.has_key(network):
574 return self.__networks[network]
575 raise Error("Unknown network \"%s\"" % network)
577 def get_bond_record(self, bond):
578 if self.__bonds.has_key(bond):
579 return self.__bonds[bond]
583 def get_vlan_record(self, vlan):
584 if self.__vlans.has_key(vlan):
585 return self.__vlans[vlan]
593 def ethtool_settings(oc):
595 if oc.has_key('ethtool-speed'):
596 val = oc['ethtool-speed']
597 if val in ["10", "100", "1000"]:
598 settings += ['speed', val]
600 log("Invalid value for ethtool-speed = %s. Must be 10|100|1000." % val)
601 if oc.has_key('ethtool-duplex'):
602 val = oc['ethtool-duplex']
603 if val in ["10", "100", "1000"]:
604 settings += ['duplex', 'val']
606 log("Invalid value for ethtool-duplex = %s. Must be half|full." % val)
607 if oc.has_key('ethtool-autoneg'):
608 val = oc['ethtool-autoneg']
609 if val in ["true", "on"]:
610 settings += ['autoneg', 'on']
611 elif val in ["false", "off"]:
612 settings += ['autoneg', 'off']
614 log("Invalid value for ethtool-autoneg = %s. Must be on|true|off|false." % val)
616 for opt in ("rx", "tx", "sg", "tso", "ufo", "gso"):
617 if oc.has_key("ethtool-" + opt):
618 val = oc["ethtool-" + opt]
619 if val in ["true", "on"]:
620 offload += [opt, 'on']
621 elif val in ["false", "off"]:
622 offload += [opt, 'off']
624 log("Invalid value for ethtool-%s = %s. Must be on|true|off|false." % (opt, val))
625 return settings,offload
627 # By default the MTU is taken from the Network.MTU setting for VIF,
628 # PIF and Bridge. However it is possible to override this by using
629 # {VIF,PIF,Network}.other-config:mtu.
631 # type parameter is a string describing the object that the oc parameter
632 # is from. e.g. "PIF", "Network"
633 def mtu_setting(nw, type, oc):
636 nwrec = db().get_network_record(nw)
637 if nwrec.has_key('MTU'):
642 if oc.has_key('mtu'):
643 log("Override Network.MTU setting on bridge %s from %s.MTU is %s" % \
644 (nwrec['bridge'], type, mtu))
649 int(mtu) # Check that the value is an integer
651 except ValueError, x:
652 log("Invalid value for mtu = %s" % mtu)
657 # IP Network Devices -- network devices with IP configuration
659 def pif_ipdev_name(pif):
660 """Return the ipdev name associated with pif"""
661 pifrec = db().get_pif_record(pif)
662 nwrec = db().get_network_record(pifrec['network'])
665 # TODO: sanity check that nwrec['bridgeless'] != 'true'
666 return nwrec['bridge']
668 # TODO: sanity check that nwrec['bridgeless'] == 'true'
669 return pif_netdev_name(pif)
672 # Bare Network Devices -- network devices without IP configuration
675 def netdev_exists(netdev):
676 return os.path.exists(root_prefix() + "/sys/class/net/" + netdev)
678 def pif_netdev_name(pif):
679 """Get the netdev name for a PIF."""
681 pifrec = db().get_pif_record(pif)
684 return "%(device)s.%(VLAN)s" % pifrec
686 return pifrec['device']
692 def pif_is_bridged(pif):
693 pifrec = db().get_pif_record(pif)
694 nwrec = db().get_network_record(pifrec['network'])
697 # TODO: sanity check that nwrec['bridgeless'] != 'true'
700 # TODO: sanity check that nwrec['bridgeless'] == 'true'
703 def pif_bridge_name(pif):
704 """Return the bridge name of a pif.
706 PIF must be a bridged PIF."""
707 pifrec = db().get_pif_record(pif)
709 nwrec = db().get_network_record(pifrec['network'])
712 return nwrec['bridge']
714 raise Error("PIF %(uuid)s does not have a bridge name" % pifrec)
719 def pif_is_bond(pif):
720 pifrec = db().get_pif_record(pif)
722 return len(pifrec['bond_master_of']) > 0
724 def pif_get_bond_masters(pif):
725 """Returns a list of PIFs which are bond masters of this PIF"""
727 pifrec = db().get_pif_record(pif)
729 bso = pifrec['bond_slave_of']
731 # bond-slave-of is currently a single reference but in principle a
732 # PIF could be a member of several bonds which are not
733 # concurrently attached. Be robust to this possibility.
734 if not bso or bso == "OpaqueRef:NULL":
736 elif not type(bso) == list:
739 bondrecs = [db().get_bond_record(bond) for bond in bso]
740 bondrecs = [rec for rec in bondrecs if rec]
742 return [bond['master'] for bond in bondrecs]
744 def pif_get_bond_slaves(pif):
745 """Returns a list of PIFs which make up the given bonded pif."""
747 pifrec = db().get_pif_record(pif)
749 bmo = pifrec['bond_master_of']
751 raise Error("Bond-master-of contains too many elements")
756 bondrec = db().get_bond_record(bmo[0])
758 raise Error("No bond record for bond master PIF")
760 return bondrec['slaves']
766 def pif_is_vlan(pif):
767 return db().get_pif_record(pif)['VLAN'] != '-1'
769 def pif_get_vlan_slave(pif):
770 """Find the PIF which is the VLAN slave of pif.
772 Returns the 'physical' PIF underneath the a VLAN PIF @pif."""
774 pifrec = db().get_pif_record(pif)
776 vlan = pifrec['VLAN_master_of']
777 if not vlan or vlan == "OpaqueRef:NULL":
778 raise Error("PIF is not a VLAN master")
780 vlanrec = db().get_vlan_record(vlan)
782 raise Error("No VLAN record found for PIF")
784 return vlanrec['tagged_PIF']
786 def pif_get_vlan_masters(pif):
787 """Returns a list of PIFs which are VLANs on top of the given pif."""
789 pifrec = db().get_pif_record(pif)
790 vlans = [db().get_vlan_record(v) for v in pifrec['VLAN_slave_of']]
791 return [v['untagged_PIF'] for v in vlans if v and db().pif_exists(v['untagged_PIF'])]
794 # Datapath base class
797 class Datapath(object):
798 """Object encapsulating the actions necessary to (de)configure the
799 datapath for a given PIF. Does not include configuration of the
800 IP address on the ipdev.
803 def __init__(self, pif):
808 """Class method called when write action is called. Can be used
809 to update any backend specific configuration."""
812 def configure_ipdev(self, cfg):
813 """Write ifcfg TYPE field for an IPdev, plus any type specific
816 raise NotImplementedError
818 def preconfigure(self, parent):
819 """Prepare datapath configuration for PIF, but do not actually
822 Any configuration files should be attached to parent.
824 raise NotImplementedError
826 def bring_down_existing(self):
827 """Tear down any existing network device configuration which
828 needs to be undone in order to bring this PIF up.
830 raise NotImplementedError
833 """Apply the configuration prepared in the preconfigure stage.
835 Should assume any configuration files changed attached in
836 the preconfigure stage are applied and bring up the
837 necesary devices to provide the datapath for the
840 Should not bring up the IPdev.
842 raise NotImplementedError
845 """Called after the IPdev has been brought up.
847 Should do any final setup, including reinstating any
848 devices which were taken down in the bring_down_existing
851 raise NotImplementedError
853 def bring_down(self):
854 """Tear down and deconfigure the datapath. Should assume the
855 IPdev has already been brought down.
857 raise NotImplementedError
859 def DatapathFactory():
860 # XXX Need a datapath object for bridgeless PIFs
863 network_conf = open(root_prefix() + "/etc/xensource/network.conf", 'r')
864 network_backend = network_conf.readline().strip()
867 raise Error("failed to determine network backend:" + e)
869 if network_backend == "bridge":
870 from InterfaceReconfigureBridge import DatapathBridge
871 return DatapathBridge
872 elif network_backend in ["openvswitch", "vswitch"]:
873 from InterfaceReconfigureVswitch import DatapathVswitch
874 return DatapathVswitch
876 raise Error("unknown network backend %s" % network_backend)