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.campbell@xxxxxxxxxx>
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@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/mailman/listinfo/xen-api
|