to an explicit whitelist. This also introduces the infrastructure for saving and reading the DatabaseCache as an XML document. This functionality isn't used yet.
Signed-off-by: Ian Campbell <ian.campb...@citrix.com> diff -r 5a6964b8b30b -r 1fd2f2aecb8c scripts/interface-reconfigure --- a/scripts/interface-reconfigure Fri Dec 18 14:16:32 2009 +0000 +++ b/scripts/interface-reconfigure Fri Dec 18 14:16:32 2009 +0000 @@ -49,6 +49,8 @@ import traceback import time import re +from xml.dom.minidom import getDOMImplementation +from xml.dom.minidom import parse as parseXML db = None management_pif = None @@ -397,7 +399,151 @@ return f +# +# Helper functions for encoding/decoding database attributes to/from XML. +# + +def str_to_xml(xml, parent, tag, val): + e = xml.createElement(tag) + parent.appendChild(e) + v = xml.createTextNode(val) + e.appendChild(v) +def str_from_xml(n): + def getText(nodelist): + rc = "" + for node in nodelist: + if node.nodeType == node.TEXT_NODE: + rc = rc + node.data + return rc + return getText(n.childNodes).strip() + +def bool_to_xml(xml, parent, tag, val): + if val: + str_to_xml(xml, parent, tag, "True") + else: + str_to_xml(xml, parent, tag, "False") +def bool_from_xml(n): + s = str_from_xml(n) + if s == "True": + return True + elif s == "False": + return False + else: + raise Error("Unknown boolean value %s" % s) + +def strlist_to_xml(xml, parent, ltag, itag, val): + e = xml.createElement(ltag) + parent.appendChild(e) + for v in val: + c = xml.createElement(itag) + e.appendChild(c) + cv = xml.createTextNode(v) + c.appendChild(cv) +def strlist_from_xml(n, ltag, itag): + ret = [] + for n in n.childNodes: + if n.nodeName == itag: + ret.append(str_from_xml(n)) + return ret + +def otherconfig_to_xml(xml, parent, val, attrs): + otherconfig = xml.createElement("other_config") + parent.appendChild(otherconfig) + for n,v in val.items(): + if not n in attrs: + raise Error("Unknown other-config attribute: %s" % n) + str_to_xml(xml, otherconfig, n, v) +def otherconfig_from_xml(n, attrs): + ret = {} + for n in n.childNodes: + if n.nodeName in attrs: + ret[n.nodeName] = str_from_xml(n) + return ret + +# +# Definitions of the database objects (and their attributes) used by interface-reconfigure. +# +# Each object is defined by a dictionary mapping an attribute name in +# the xapi database to a tuple containing two items: +# - a function which takes this attribute and encodes it as XML. +# - a function which takes XML and decocdes it into a value. +# +# other-config attributes are specified as a simple array of strings + +PIF_XML_TAG = "pif" +VLAN_XML_TAG = "vlan" +BOND_XML_TAG = "bond" +NETWORK_XML_TAG = "network" + +ETHTOOL_OTHERCONFIG_ATTRS = ['ethtool-%s' % x for x in 'autoneg', 'speed', 'duplex', 'rx', 'tx', 'sg', 'tso', 'ufo', 'gso' ] + +PIF_OTHERCONFIG_ATTRS = [ 'domain', 'peerdns', 'defaultroute', 'mtu', 'static-routes' ] + \ + [ 'bond-%s' % x for x in 'mode', 'miimon', 'downdelay', 'updelay', 'use_carrier' ] + \ + ETHTOOL_OTHERCONFIG_ATTRS + +PIF_ATTRS = { 'uuid': (str_to_xml,str_from_xml), + 'management': (bool_to_xml,bool_from_xml), + 'network': (str_to_xml,str_from_xml), + 'device': (str_to_xml,str_from_xml), + 'bond_master_of': (lambda x, p, t, v: strlist_to_xml(x, p, 'bond_master_of', 'slave', v), + lambda n: strlist_from_xml(n, 'bond_master_of', 'slave')), + 'bond_slave_of': (str_to_xml,str_from_xml), + 'VLAN': (str_to_xml,str_from_xml), + 'VLAN_master_of': (str_to_xml,str_from_xml), + 'VLAN_slave_of': (lambda x, p, t, v: strlist_to_xml(x, p, 'VLAN_slave_of', 'master', v), + lambda n: strlist_from_xml(n, 'VLAN_slave_Of', 'master')), + 'ip_configuration_mode': (str_to_xml,str_from_xml), + 'IP': (str_to_xml,str_from_xml), + 'netmask': (str_to_xml,str_from_xml), + 'gateway': (str_to_xml,str_from_xml), + 'DNS': (str_to_xml,str_from_xml), + 'MAC': (str_to_xml,str_from_xml), + 'other_config': (lambda x, p, t, v: otherconfig_to_xml(x, p, v, PIF_OTHERCONFIG_ATTRS), + lambda n: otherconfig_from_xml(n, PIF_OTHERCONFIG_ATTRS)), + + # Special case: We write the current value + # PIF.currently-attached to the cache but since it will + # not be valid when we come to use the cache later + # (i.e. after a reboot) we always read it as False. + 'currently_attached': (bool_to_xml, lambda n: False), + } + +VLAN_ATTRS = { 'uuid': (str_to_xml,str_from_xml), + 'tagged_PIF': (str_to_xml,str_from_xml), + 'untagged_PIF': (str_to_xml,str_from_xml), + } + +BOND_ATTRS = { 'uuid': (str_to_xml,str_from_xml), + 'master': (str_to_xml,str_from_xml), + 'slaves': (lambda x, p, t, v: strlist_to_xml(x, p, 'slaves', 'slave', v), + lambda n: strlist_from_xml(n, 'slaves', 'slave')), + } + +NETWORK_OTHERCONFIG_ATTRS = [ 'mtu', 'static-routes' ] + ETHTOOL_OTHERCONFIG_ATTRS + +NETWORK_ATTRS = { 'uuid': (str_to_xml,str_from_xml), + 'bridge': (str_to_xml,str_from_xml), + 'PIFs': (lambda x, p, t, v: strlist_to_xml(x, p, 'PIFs', 'PIF', v), + lambda n: strlist_from_xml(n, 'PIFs', 'PIF')), + 'other_config': (lambda x, p, t, v: otherconfig_to_xml(x, p, v, NETWORK_OTHERCONFIG_ATTRS), + lambda n: otherconfig_from_xml(n, NETWORK_OTHERCONFIG_ATTRS)), + } + +# +# Database Cache object +# + class DatabaseCache(object): + def __read_xensource_inventory(self): + filename = "/etc/xensource-inventory" + f = open(filename, "r") + lines = [x.strip("\n") for x in f.readlines()] + f.close() + + defs = [ (l[:l.find("=")], l[(l.find("=") + 1):]) for l in lines ] + defs = [ (a, b.strip("'")) for (a,b) in defs ] + + return dict(defs) def __pif_on_host(self,pif): return self.__pifs.has_key(pif) @@ -406,7 +552,13 @@ for (p,rec) in session.xenapi.PIF.get_all_records().items(): if rec['host'] != host: continue - self.__pifs[p] = rec + self.__pifs[p] = {} + for f in PIF_ATTRS: + self.__pifs[p][f] = rec[f] + self.__pifs[p]['other_config'] = {} + for f in PIF_OTHERCONFIG_ATTRS: + if not rec['other_config'].has_key(f): continue + self.__pifs[p]['other_config'][f] = rec['other_config'][f] def __get_vlan_records_from_xapi(self, session): self.__vlans = {} @@ -414,7 +566,9 @@ rec = session.xenapi.VLAN.get_record(v) if not self.__pif_on_host(rec['untagged_PIF']): continue - self.__vlans[v] = rec + self.__vlans[v] = {} + for f in VLAN_ATTRS: + self.__vlans[v][f] = rec[f] def __get_bond_records_from_xapi(self, session): self.__bonds = {} @@ -422,18 +576,53 @@ rec = session.xenapi.Bond.get_record(b) if not self.__pif_on_host(rec['master']): continue - self.__bonds[b] = rec + self.__bonds[b] = {} + for f in BOND_ATTRS: + self.__bonds[b][f] = rec[f] def __get_network_records_from_xapi(self, session): self.__networks = {} for n in session.xenapi.network.get_all(): rec = session.xenapi.network.get_record(n) - self.__networks[n] = rec - # drop PIFs on other hosts - self.__networks[n]["PIFs"] = [p for p in rec["PIFs"] if self.__pif_on_host(p)] + self.__networks[n] = {} + for f in NETWORK_ATTRS: + if f == "PIFs": + # drop PIFs on other hosts + self.__networks[n][f] = [p for p in rec[f] if self.__pif_on_host(p)] + else: + self.__networks[n][f] = rec[f] + self.__networks[n]['other_config'] = {} + for f in NETWORK_OTHERCONFIG_ATTRS: + if not rec['other_config'].has_key(f): continue + self.__networks[n]['other_config'][f] = rec['other_config'][f] - def __init__(self, session_ref): - # XXX extra level of indendation temporarily reduces diff until XML cache patch + def __to_xml(self, xml, parent, key, ref, rec, attrs): + """Encode a database object as XML""" + e = xml.createElement(key) + parent.appendChild(e) + if ref: + e.setAttribute('ref', ref) + + for n,v in rec.items(): + if attrs.has_key(n): + h,_ = attrs[n] + h(xml, e, n, v) + else: + raise Error("Unknown attribute %s" % n) + def __from_xml(self, e, attrs): + """Decode a database object from XML""" + ref = e.attributes['ref'].value + rec = {} + for n in e.childNodes: + if n.nodeName in attrs: + _,h = attrs[n.nodeName] + rec[n.nodeName] = h(n) + return (ref,rec) + + def __init__(self, session_ref=None, cache_file=None): + if session_ref and cache_file: + raise Error("can't specify session reference and cache file") + if cache_file == None: session = XenAPI.xapi_local() if not session_ref: @@ -458,6 +647,56 @@ finally: if not session_ref: session.xenapi.session.logout() + else: + log("Loading xapi database cache from %s" % cache_file) + + xml = parseXML(cache_file) + + self.__pifs = {} + self.__bonds = {} + self.__vlans = {} + self.__networks = {} + + assert(len(xml.childNodes) == 1) + toplevel = xml.childNodes[0] + + assert(toplevel.nodeName == "xenserver-network-configuration") + + for n in toplevel.childNodes: + if n.nodeName == "#text": + pass + elif n.nodeName == PIF_XML_TAG: + (ref,rec) = self.__from_xml(n, PIF_ATTRS) + self.__pifs[ref] = rec + elif n.nodeName == BOND_XML_TAG: + (ref,rec) = self.__from_xml(n, BOND_ATTRS) + self.__bonds[ref] = rec + elif n.nodeName == VLAN_XML_TAG: + (ref,rec) = self.__from_xml(n, VLAN_ATTRS) + self.__vlans[ref] = rec + elif n.nodeName == NETWORK_XML_TAG: + (ref,rec) = self.__from_xml(n, NETWORK_ATTRS) + self.__networks[ref] = rec + else: + raise Error("Unknown XML element %s" % n.nodeName) + + def save(self, cache_file): + + xml = getDOMImplementation().createDocument( + None, "xenserver-network-configuration", None) + for (ref,rec) in self.__pifs.items(): + self.__to_xml(xml, xml.documentElement, PIF_XML_TAG, ref, rec, PIF_ATTRS) + for (ref,rec) in self.__bonds.items(): + self.__to_xml(xml, xml.documentElement, BOND_XML_TAG, ref, rec, BOND_ATTRS) + for (ref,rec) in self.__vlans.items(): + self.__to_xml(xml, xml.documentElement, VLAN_XML_TAG, ref, rec, VLAN_ATTRS) + for (ref,rec) in self.__networks.items(): + self.__to_xml(xml, xml.documentElement, NETWORK_XML_TAG, ref, rec, + NETWORK_ATTRS) + + f = open(cache_file, 'w') + f.write(xml.toprettyxml()) + f.close() def get_pif_by_uuid(self, uuid): pifs = map(lambda (ref,rec): ref, _______________________________________________ xen-api mailing list xen-api@lists.xensource.com http://lists.xensource.com/mailman/listinfo/xen-api