[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

Re: [et-mgmt-tools] [patch 7/8] Add CapabilitiesParser module



Hey Dan,
	All good points. How does this look? I cut out the validation stuff,
added tests and changed the comment about try/except/finally.

	(I'm ignoring the post-install-check and requiring-initrd comments for
now ... as you say, they're problems with the existing code)

Cheers,
Mark.
Subject: Add CapabilitiesParser module
From: Mark McLoughlin <markmc redhat com>

This adds a simple module for parsing libvirt's getCapabilities()
XML.

It might seem more straightforward to just use XPath for this, but
in the code to support arbitrary system images, you iterate over
the various ways that the image can be booted and the various ways
in which a hypervisor can boot a guest and find the best match. For
that kind of use, the API below is much easier to use.

Note, this parser rejects things like architectures it doesn't
know about, which needs to be fixed in order to have forward
compatibility.

Signed-off-by: Mark McLoughlin <markmc redhat com>

Index: virtinst--devel/virtinst/CapabilitiesParser.py
===================================================================
--- /dev/null
+++ virtinst--devel/virtinst/CapabilitiesParser.py
@@ -0,0 +1,162 @@
+#!/usr/bin/python -tt
+
+# Some code for parsing libvirt's capabilities XML
+#
+# Copyright 2007  Red Hat, Inc.
+# Mark McLoughlin <markmc redhat com>
+#
+# This software may be freely redistributed under the terms of the GNU
+# general public license.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+import libxml2
+
+class CapabilitiesParserException(Exception):
+    def __init__(self, msg):
+        Exception.__init__(self, msg)
+
+FEATURE_ACPI    = 0x01
+FEATURE_APIC    = 0x02
+FEATURE_PAE     = 0x04
+FEATURE_NONPAE  = 0x08
+FEATURE_VMX     = 0x10
+FEATURE_SVM     = 0x20
+FEATURE_IA64_BE = 0x40
+
+features_map = {
+    "acpi"    : FEATURE_ACPI,
+    "apic"    : FEATURE_APIC,
+    "pae"     : FEATURE_PAE,
+    "nonpae"  : FEATURE_NONPAE,
+    "vmx"     : FEATURE_VMX,
+    "svm"     : FEATURE_SVM,
+    "ia64_be" : FEATURE_IA64_BE
+}
+
+NUM_FEATURES = len(features_map)
+
+def _parse_features(self, node):
+    features = 0
+
+    child = node.children
+    while child:
+        if child.name in features_map:
+            features |= features_map[child.name]
+
+        child = child.next
+
+    return features
+
+class Host(object):
+    def __init__(self, node = None):
+        # e.g. "i686" or "x86_64"
+        self.arch = None
+
+        # e.g. FEATURE_HVM|FEATURE_ACPI
+        self.features = 0
+
+        if not node is None:
+            self.parseXML(node)
+
+    def parseXML(self, node):
+        child = node.children
+        while child:
+            if child.name != "cpu":
+                child = child.next
+                continue
+
+            n = child.children
+            while n:
+                if n.name == "arch":
+                    self.arch = n.content
+                elif n.name == "features":
+                    self.features |= _parse_features(self, n)
+                n = n.next
+
+            child = child.next
+
+class Guest(object):
+    def __init__(self, node = None):
+        # e.g. "xen" or "hvm"
+        self.os_type = None
+
+        # e.g. "xen", "qemu", "kqemu" or "kvm"
+        self.hypervisor_type = None
+
+        # e.g. "i686" or "x86_64"
+        self.arch = None
+
+        # e.g. FEATURE_HVM|FEATURE_ACPI
+        self.features = 0
+
+        if not node is None:
+            self.parseXML(node)
+
+    def parseXML(self, node):
+        child = node.children
+        while child:
+            if child.name == "os_type":
+                self.os_type = child.content
+            elif child.name == "features":
+                self.features |= _parse_features(self, child)
+            elif child.name == "arch":
+                self.arch = child.prop("name")
+                n = child.children
+                while n:
+                    # NB. for now, ignoring the rest of arch e.g. wordsize etc.
+                    if n.name == "domain":
+                        self.hypervisor_type = n.prop("type")
+                    n = n.next
+
+            child = child.next
+
+class Capabilities(object):
+    def __init__(self, node = None):
+        self.host = None
+        self.guests = []
+
+        if not node is None:
+            self.parseXML(node)
+
+    def parseXML(self, node):
+        child = node.children
+        while child:
+            if child.name == "host":
+                self.host = Host(child)
+            elif child.name == "guest":
+                self.guests.append(Guest(child))
+            child = child.next
+
+def parse(xml):
+    class ErrorHandler:
+        def __init__(self):
+            self.msg = ""
+        def handler(self, ctx, str):
+            self.msg += str
+    error = ErrorHandler()
+    libxml2.registerErrorHandler(error.handler, None)
+
+    try:
+        # try/except/finally is only available in python-2.5
+        try:
+            doc = libxml2.readMemory(xml, len(xml),
+                                     None, None,
+                                     libxml2.XML_PARSE_NOBLANKS)
+        except (libxml2.parserError, libxml2.treeError), e:
+            raise CapabilitiesParserException("%s\n%s" % (e, error.msg))
+    finally:
+        libxml2.registerErrorHandler(None, None)
+
+    try:
+        root = doc.getRootElement()
+        if root.name != "capabilities":
+            raise CapabilitiesParserException("Root element is not 'capabilties'")
+
+        capabilities = Capabilities(root)
+    finally:
+        doc.freeDoc()
+
+    return capabilities
Index: virtinst--devel/tests/capabilities-qemu.xml
===================================================================
--- /dev/null
+++ virtinst--devel/tests/capabilities-qemu.xml
@@ -0,0 +1,71 @@
+<capabilities>
+  <host>
+    <cpu>
+      <arch>x86_64</arch>
+    </cpu>
+  </host>
+
+  <guest>
+    <os_type>hvm</os_type>
+    <arch name="x86_64">
+      <wordsize>64</wordsize>
+      <emulator>/usr/bin/qemu-system-x86_64</emulator>
+      <domain type="qemu"/>
+      <machine>pc</machine>
+      <machine>isapc</machine>
+    </arch>
+  </guest>
+
+  <guest>
+    <os_type>hvm</os_type>
+    <arch name="i686">
+      <wordsize>32</wordsize>
+      <emulator>/usr/bin/qemu</emulator>
+      <domain type="qemu"/>
+      <machine>pc</machine>
+      <machine>isapc</machine>
+    </arch>
+  </guest>
+
+  <guest>
+    <os_type>hvm</os_type>
+    <arch name="mips">
+      <wordsize>32</wordsize>
+      <emulator>/usr/bin/qemu-system-mips</emulator>
+      <domain type="qemu"/>
+      <machine>mips</machine>
+    </arch>
+  </guest>
+
+  <guest>
+    <os_type>hvm</os_type>
+    <arch name="mipsel">
+      <wordsize>32</wordsize>
+      <emulator>/usr/bin/qemu-system-mipsel</emulator>
+      <domain type="qemu"/>
+      <machine>mips</machine>
+    </arch>
+  </guest>
+
+  <guest>
+    <os_type>hvm</os_type>
+    <arch name="sparc">
+      <wordsize>32</wordsize>
+      <emulator>/usr/bin/qemu-system-sparc</emulator>
+      <domain type="qemu"/>
+      <machine>sun4m</machine>
+    </arch>
+  </guest>
+
+  <guest>
+    <os_type>hvm</os_type>
+    <arch name="ppc">
+      <wordsize>32</wordsize>
+      <emulator>/usr/bin/qemu-system-ppc</emulator>
+      <domain type="qemu"/>
+      <machine>g3bw</machine>
+      <machine>mac99</machine>
+      <machine>prep</machine>
+    </arch>
+  </guest>
+</capabilities>
Index: virtinst--devel/tests/capabilities-test.xml
===================================================================
--- /dev/null
+++ virtinst--devel/tests/capabilities-test.xml
@@ -0,0 +1,23 @@
+<capabilities>
+  <host>
+    <cpu>
+      <arch>i686</arch>
+      <features>
+        <pae/>
+        <nonpae/>
+      </features>
+    </cpu>
+  </host>
+
+  <guest>
+    <os_type>linux</os_type>
+    <arch name="i686">
+      <wordsize>32</wordsize>
+      <domain type="test"/>
+    </arch>
+    <features>
+      <pae/>
+      <nonpae/>
+    </features>
+  </guest>
+</capabilities>
Index: virtinst--devel/tests/capabilities-xen.xml
===================================================================
--- /dev/null
+++ virtinst--devel/tests/capabilities-xen.xml
@@ -0,0 +1,61 @@
+<capabilities>
+  <host>
+    <cpu>
+      <arch>x86_64</arch>
+      <features>
+        <vmx/>
+      </features>
+    </cpu>
+  </host>
+
+  <guest>
+    <os_type>xen</os_type>
+    <arch name="x86_64">
+      <wordsize>64</wordsize>
+      <domain type="xen"></domain>
+    </arch>
+    <features>
+    </features>
+  </guest>
+
+  <guest>
+    <os_type>xen</os_type>
+    <arch name="i686">
+      <wordsize>32</wordsize>
+      <domain type="xen"></domain>
+    </arch>
+    <features>
+      <pae/>
+    </features>
+  </guest>
+
+  <guest>
+    <os_type>hvm</os_type>
+    <arch name="i686">
+      <wordsize>32</wordsize>
+      <domain type="xen"></domain>
+      <emulator>/usr/lib/xen/bin/qemu-dm</emulator>
+      <machine>pc</machine>
+      <machine>isapc</machine>
+      <loader>/usr/lib/xen/boot/hvmloader</loader>
+    </arch>
+    <features>
+      <pae/>
+      <nonpae/>
+    </features>
+  </guest>
+
+  <guest>
+    <os_type>hvm</os_type>
+    <arch name="x86_64">
+      <wordsize>64</wordsize>
+      <domain type="xen"></domain>
+      <emulator>/usr/lib64/xen/bin/qemu-dm</emulator>
+      <machine>pc</machine>
+      <machine>isapc</machine>
+      <loader>/usr/lib/xen/boot/hvmloader</loader>
+    </arch>
+    <features>
+    </features>
+  </guest>
+</capabilities>
Index: virtinst--devel/tests/capabilities.py
===================================================================
--- /dev/null
+++ virtinst--devel/tests/capabilities.py
@@ -0,0 +1,57 @@
+import os.path
+import unittest
+import virtinst.CapabilitiesParser as capabilities
+
+class TestCapabilities(unittest.TestCase):
+
+    def _compareGuest(self, (arch, os_type, hypervisor_type, features), guest):
+        self.assertEqual(arch,            guest.arch)
+        self.assertEqual(os_type,         guest.os_type)
+        self.assertEqual(hypervisor_type, guest.hypervisor_type)
+        self.assertEqual(features,        guest.features)
+
+    def _testCapabilities(self, path, (host_arch, host_features), guests):
+        caps = capabilities.parse(file(os.path.join("tests", path)).read())
+
+        self.assertEqual(host_arch,     caps.host.arch)
+        self.assertEqual(host_features, caps.host.features)
+
+        map(self._compareGuest, guests, caps.guests)
+
+    def testCapabilities1(self):
+        host = ( 'x86_64', capabilities.FEATURE_VMX )
+
+        guests = [
+            ( 'x86_64', 'xen', 'xen', 0 ),
+            ( 'i686',   'xen', 'xen', capabilities.FEATURE_PAE ),
+            ( 'i686',   'hvm', 'xen', capabilities.FEATURE_PAE|capabilities.FEATURE_NONPAE ),
+            ( 'x86_64', 'hvm', 'xen', 0 )
+        ]
+
+        self._testCapabilities("capabilities-xen.xml", host, guests)
+
+    def testCapabilities2(self):
+        host = ( 'x86_64', 0 )
+
+        guests = [
+            ( 'x86_64', 'hvm', 'qemu', 0 ),
+            ( 'i686',   'hvm', 'qemu', 0 ),
+            ( 'mips',   'hvm', 'qemu', 0 ),
+            ( 'mipsel', 'hvm', 'qemu', 0 ),
+            ( 'sparc',  'hvm', 'qemu', 0 ),
+            ( 'ppc',    'hvm', 'qemu', 0 ),
+        ]
+
+        self._testCapabilities("capabilities-qemu.xml", host, guests)
+
+    def testCapabilities3(self):
+        host = ( 'i686', capabilities.FEATURE_PAE|capabilities.FEATURE_NONPAE )
+
+        guests = [
+            ( 'i686', 'linux', 'test', capabilities.FEATURE_PAE|capabilities.FEATURE_NONPAE ),
+        ]
+
+        self._testCapabilities("capabilities-test.xml", host, guests)
+
+if __name__ == "__main__":
+    unittest.main()

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]