WARNING - OLD ARCHIVES

This is an archived copy of the Xen.org mailing list, which we have preserved to ensure that existing links to archives are not broken. The live archive, which contains the latest emails, can be found at http://lists.xen.org/
   
 
 
Xen 
 
Home Products Support Community News
 
   
 

xen-api

[Xen-API] [PATCH 05 of 33] interface-reconfigure: Restrict the fields pu

To: xen-api@xxxxxxxxxxxxxxxxxxx
Subject: [Xen-API] [PATCH 05 of 33] interface-reconfigure: Restrict the fields pulled from XenAPI into DatabaseCache
From: Ian Campbell <ian.campbell@xxxxxxxxxx>
Date: Fri, 18 Dec 2009 14:17:00 +0000
Cc: Ian Campbell <ian.campbell@xxxxxxxxxx>
Delivery-date: Fri, 18 Dec 2009 06:18:04 -0800
Envelope-to: www-data@xxxxxxxxxxxxxxxxxxx
In-reply-to: <patchbomb.1261145815@xxxxxxxxxxxxxxxxxxxxxx>
List-help: <mailto:xen-api-request@lists.xensource.com?subject=help>
List-id: Discussion of API issues surrounding Xen <xen-api.lists.xensource.com>
List-post: <mailto:xen-api@lists.xensource.com>
List-subscribe: <http://lists.xensource.com/mailman/listinfo/xen-api>, <mailto:xen-api-request@lists.xensource.com?subject=subscribe>
List-unsubscribe: <http://lists.xensource.com/mailman/listinfo/xen-api>, <mailto:xen-api-request@lists.xensource.com?subject=unsubscribe>
Sender: xen-api-bounces@xxxxxxxxxxxxxxxxxxx
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

<Prev in Thread] Current Thread [Next in Thread>