[libvirt] [PATCH for 1.2.7 1/8] Introduce domain_capabilities

Michal Privoznik mprivozn at redhat.com
Mon Jun 30 15:31:44 UTC 2014


This new module holds and formats capabilities for emulator. If you
are about to create a new domain, you may want to know what is the
host or hypervisor capable of. To make sure we don't regress on the
XML, the formatting is not something left for each driver to
implement, rather there's general format function.

The domain capabilities is a lockable object (even though the locking
is not necessary yet) which uses reference counter.

Signed-off-by: Michal Privoznik <mprivozn at redhat.com>
---
 docs/formatdomaincaps.html.in                   | 200 +++++++++++++++++++
 docs/schemas/Makefile.am                        |   1 +
 docs/schemas/domaincaps.rng                     |  90 +++++++++
 docs/sitemap.html.in                            |   4 +
 libvirt.spec.in                                 |   1 +
 mingw-libvirt.spec.in                           |   2 +
 po/POTFILES.in                                  |   1 +
 src/Makefile.am                                 |   1 +
 src/conf/domain_capabilities.c                  | 254 ++++++++++++++++++++++++
 src/conf/domain_capabilities.h                  | 103 ++++++++++
 src/libvirt_private.syms                        |   7 +
 tests/Makefile.am                               |   8 +
 tests/domaincapsschemadata/domaincaps-basic.xml |  10 +
 tests/domaincapsschemadata/domaincaps-full.xml  |  56 ++++++
 tests/domaincapsschematest                      |  11 +
 tests/domaincapstest.c                          | 149 ++++++++++++++
 16 files changed, 898 insertions(+)
 create mode 100644 docs/formatdomaincaps.html.in
 create mode 100644 docs/schemas/domaincaps.rng
 create mode 100644 src/conf/domain_capabilities.c
 create mode 100644 src/conf/domain_capabilities.h
 create mode 100644 tests/domaincapsschemadata/domaincaps-basic.xml
 create mode 100644 tests/domaincapsschemadata/domaincaps-full.xml
 create mode 100755 tests/domaincapsschematest
 create mode 100644 tests/domaincapstest.c

diff --git a/docs/formatdomaincaps.html.in b/docs/formatdomaincaps.html.in
new file mode 100644
index 0000000..cfd61d9
--- /dev/null
+++ b/docs/formatdomaincaps.html.in
@@ -0,0 +1,200 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <body>
+    <h1>Domain capabilities XML format</h1>
+
+    <ul id="toc"></ul>
+
+    <h2><a name="Motivation">Motivation</a></h2>
+
+    <p>Sometimes, when a new domain is to be created it may come handy to know
+    the capabilities of the hypervisor so the correct combination of devices and
+    drivers is used. For example, when management application is considering the
+    mode for a host device's passthrough there are several options depending not
+    only on host, but on hypervisor in question too. If the hypervisor is qemu
+    then it needs to be more recent to support VFIO, while legacy KVM is
+    achievable just fine with older one.</p>
+
+    <p>The main difference between <a
+        href="formatcaps.html">virConnectGetCapabilities</a> and the emulator
+    capabilities API is, the former one aims more on the host capabilities (e.g.
+    NUMA topology, security models in effect, etc.) while the latter one
+    specializes on the hypervisor capabilities.</p>
+
+    <h2><a name="elements">Element and attribute overview</a></h2>
+
+    <p>The root element that emulator capability XML document starts with has
+    name <code>domainCapabilities</code>. It contains at least four direct
+    child elements:</p>
+
+<pre>
+<domainCapabilities>
+  <path>/usr/bin/qemu-system-x86_64</path>
+  <domain>kvm</domain>
+  <machine>pc-i440fx-2.1</machine>
+  <arch>x86_64</arch>
+  ...
+</domainCapabilities>
+</pre>
+    <dl>
+      <dt>path</dt>
+      <dd>The full path to the emulator binary.</dd>
+
+      <dt>domain</dt>
+      <dd>Describes the <a href="formatdomain.html#elements">virtualization
+          type</a> (or so called domain type).</dd>
+
+      <dt>machine</dt>
+      <dd>The domain's <a href="formatdomain.html#elementsOSBIOS">machine
+          type</a>.</dd>
+
+      <dt>arch</dt>
+      <dd>The domain's <a href="formatdomain.html#elementsOSBIOS">
+          architecture</a>.</dd>
+
+    </dl>
+
+    <h3><a name="elementsCPUAllocation">CPU Allocation</a></h3>
+
+<pre>
+<domainCapabilities>
+  ...
+  <vcpu max='255'/>
+  ...
+</domainCapabilities>
+</pre>
+
+    <dl>
+      <dt>vcpu</dt>
+      <dd>The maximum number of supported virtual CPUs</dd>
+    </dl>
+
+    <h3><a name="elementsDevices">Devices</a></h3>
+
+    <p>
+      The final set of XML elements are all used to describe supported devices
+      and their capabilities. All devices occur as children of the main
+      <code>devices</code> element.
+    </p>
+
+<pre>
+<domainCapabilities>
+  ...
+  <devices>
+    <disk supported='yes'>
+      <enum name='diskDevice'>
+        <value>disk</value>
+        <value>cdrom</value>
+        <value>floppy</value>
+        <value>lun</value>
+      </enum>
+      ...
+    </disk>
+    <hostdev supported='no'/>
+  </devices>
+</domainCapabilities>
+</pre>
+
+    <p>All reported capabilities share the way that they are expressed. In the
+    example above we can see what disk devices are supported (<code>disk</code>,
+    <code>cdrom</code>, <code>floppy</code> and <code>lun</code>).</p>
+
+    <h4><a name="elementsDisks">Hard drives, floppy disks, CDROMs</a></h4>
+    <p>Disk capabilities are exposed under <code>disk</code> element. For
+    instance:</p>
+
+<pre>
+<domainCapabilities>
+  ...
+  <devices>
+    <disk supported='yes'>
+      <enum name='diskDevice'>
+        <value>disk</value>
+        <value>cdrom</value>
+        <value>floppy</value>
+        <value>lun</value>
+      </enum>
+      <enum name='bus'>
+        <value>ide</value>
+        <value>fdc</value>
+        <value>scsi</value>
+        <value>virtio</value>
+        <value>xen</value>
+        <value>usb</value>
+        <value>uml</value>
+        <value>sata</value>
+        <value>sd</value>
+      </enum>
+    </disk>
+    ...
+  </devices>
+</domainCapabilities>
+</pre>
+
+    <dl>
+      <dt>diskDevice</dt>
+      <dd>What disk types are supported.</dd>
+
+      <dt>bus</dt>
+      <dd>The bus within a domain that disk may occur on</dd>
+    </dl>
+
+    <h4><a name="elementsHostDev">Host device assignment</a></h4>
+    <p>Some host devices can be passed through to a guest (e.g. USB, PCI and
+    SCSI). Well, only if the following is enabled:</p>
+
+<pre>
+<domainCapabilities>
+  ...
+  <devices>
+    <hostdev supported='yes'>
+      <enum name='mode'>
+        <value>subsystem</value>
+        <value>capabilities</value>
+      </enum>
+      <enum name='startupPolicy'>
+        <value>default</value>
+        <value>mandatory</value>
+        <value>requisite</value>
+        <value>optional</value>
+      </enum>
+      <enum name='subsysType'>
+        <value>usb</value>
+        <value>pci</value>
+        <value>scsi</value>
+      </enum>
+      <enum name='capsType'>
+        <value>storage</value>
+        <value>misc</value>
+        <value>net</value>
+      </enum>
+      <enum name='pciBackend'>
+        <value>default</value>
+        <value>kvm</value>
+        <value>vfio</value>
+        <value>xen</value>
+      </enum>
+    </hostdev>
+  </devices>
+</domainCapabilities>
+</pre>
+
+    <dl>
+      <dt>mode</dt>
+      <dd>Describes what passthrough modes are supported</dd>
+
+      <dt>startupPolicy</dt>
+      <dd>What startup policy modes are known to libvirt</dd>
+
+      <dt>subsysType</dt>
+      <dd>Enumerates subsystems that are capable of passing through a device</dd>
+
+      <dt>capsType</dt>
+      <dd>host device mode</dd>
+
+      <dt>pciBackend</dt>
+      <dd>Which backend should be used to manage device passthrough</dd>
+    </dl>
+  </body>
+</html>
diff --git a/docs/schemas/Makefile.am b/docs/schemas/Makefile.am
index d71c327..0e14dc6 100644
--- a/docs/schemas/Makefile.am
+++ b/docs/schemas/Makefile.am
@@ -19,6 +19,7 @@ schema_DATA = \
 	basictypes.rng \
 	capability.rng \
 	domain.rng \
+	domaincaps.rng \
 	domaincommon.rng \
 	domainsnapshot.rng \
 	interface.rng \
diff --git a/docs/schemas/domaincaps.rng b/docs/schemas/domaincaps.rng
new file mode 100644
index 0000000..627b699
--- /dev/null
+++ b/docs/schemas/domaincaps.rng
@@ -0,0 +1,90 @@
+<?xml version="1.0"?>
+<!-- A Relax NG schema for the libvirt domain capabilities XML format -->
+<grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+  <include href='basictypes.rng'/>
+  <start>
+    <ref name='domainCapabilities'/>
+  </start>
+
+
+  <define name='domainCapabilities'>
+    <element name='domainCapabilities'>
+      <interleave>
+        <element name='path'>
+          <ref name="absFilePath"/>
+        </element>
+        <element name='domain'>
+          <text/>
+        </element>
+        <element name='machine'>
+          <text/>
+        </element>
+        <element name='arch'>
+          <text/>
+        </element>
+        <optional>
+          <ref name='vcpu'/>
+        </optional>
+        <optional>
+          <ref name='devices'/>
+        </optional>
+      </interleave>
+    </element>
+  </define>
+
+  <define name='vcpu'>
+    <element name='vcpu'>
+      <attribute name='max'>
+        <ref name='unsignedInt'/>
+      </attribute>
+      <empty/>
+    </element>
+  </define>
+
+  <define name='devices'>
+    <element name='devices'>
+      <interleave>
+        <ref name='disk'/>
+        <ref name='hostdev'/>
+      </interleave>
+    </element>
+  </define>
+
+  <define name='disk'>
+    <element name='disk'>
+      <ref name='supported'/>
+      <ref name='enum'/>
+    </element>
+  </define>
+
+  <define name='hostdev'>
+    <element name='hostdev'>
+      <ref name='supported'/>
+      <ref name='enum'/>
+    </element>
+  </define>
+
+  <define name='supported'>
+    <attribute name='supported'>
+      <choice>
+        <value>yes</value>
+        <value>no</value>
+      </choice>
+    </attribute>
+  </define>
+
+  <define name='enum'>
+    <zeroOrMore>
+      <element name='enum'>
+        <attribute name='name'>
+          <text/>
+        </attribute>
+        <zeroOrMore>
+          <element name='value'>
+            <text/>
+          </element>
+        </zeroOrMore>
+      </element>
+    </zeroOrMore>
+  </define>
+</grammar>
diff --git a/docs/sitemap.html.in b/docs/sitemap.html.in
index 78e84e3..1e91869 100644
--- a/docs/sitemap.html.in
+++ b/docs/sitemap.html.in
@@ -175,6 +175,10 @@
                 <span>The driver capabilities XML format</span>
               </li>
               <li>
+                <a href="formatdomaincaps.html">Domain capabilities</a>
+                <span>The domain capabilities XML format</span>
+              </li>
+              <li>
                 <a href="formatnode.html">Node Devices</a>
                 <span>The host device XML format</span>
               </li>
diff --git a/libvirt.spec.in b/libvirt.spec.in
index 2ec7eed..f0bf737 100644
--- a/libvirt.spec.in
+++ b/libvirt.spec.in
@@ -2164,6 +2164,7 @@ exit 0
 %{_datadir}/libvirt/schemas/basictypes.rng
 %{_datadir}/libvirt/schemas/capability.rng
 %{_datadir}/libvirt/schemas/domain.rng
+%{_datadir}/libvirt/schemas/domaincaps.rng
 %{_datadir}/libvirt/schemas/domaincommon.rng
 %{_datadir}/libvirt/schemas/domainsnapshot.rng
 %{_datadir}/libvirt/schemas/interface.rng
diff --git a/mingw-libvirt.spec.in b/mingw-libvirt.spec.in
index 91c2dc2..a555f9c 100644
--- a/mingw-libvirt.spec.in
+++ b/mingw-libvirt.spec.in
@@ -205,6 +205,7 @@ rm -rf $RPM_BUILD_ROOT%{mingw64_libexecdir}/libvirt-guests.sh
 %{mingw32_datadir}/libvirt/schemas/basictypes.rng
 %{mingw32_datadir}/libvirt/schemas/capability.rng
 %{mingw32_datadir}/libvirt/schemas/domain.rng
+%{mingw32_datadir}/libvirt/schemas/domaincaps.rng
 %{mingw32_datadir}/libvirt/schemas/domaincommon.rng
 %{mingw32_datadir}/libvirt/schemas/domainsnapshot.rng
 %{mingw32_datadir}/libvirt/schemas/interface.rng
@@ -265,6 +266,7 @@ rm -rf $RPM_BUILD_ROOT%{mingw64_libexecdir}/libvirt-guests.sh
 %{mingw64_datadir}/libvirt/schemas/basictypes.rng
 %{mingw64_datadir}/libvirt/schemas/capability.rng
 %{mingw64_datadir}/libvirt/schemas/domain.rng
+%{mingw64_datadir}/libvirt/schemas/domaincaps.rng
 %{mingw64_datadir}/libvirt/schemas/domaincommon.rng
 %{mingw64_datadir}/libvirt/schemas/domainsnapshot.rng
 %{mingw64_datadir}/libvirt/schemas/interface.rng
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 31a8381..70fb6c2 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -16,6 +16,7 @@ src/conf/capabilities.c
 src/conf/cpu_conf.c
 src/conf/device_conf.c
 src/conf/domain_addr.c
+src/conf/domain_capabilities.c
 src/conf/domain_conf.c
 src/conf/domain_event.c
 src/conf/interface_conf.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 2b9ac61..e81af0c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -248,6 +248,7 @@ NETDEV_CONF_SOURCES =						\
 DOMAIN_CONF_SOURCES =						\
 		conf/capabilities.c conf/capabilities.h		\
 		conf/domain_addr.c conf/domain_addr.h		\
+		conf/domain_capabilities.c conf/domain_capabilities.h	\
 		conf/domain_conf.c conf/domain_conf.h		\
 		conf/domain_audit.c conf/domain_audit.h		\
 		conf/domain_nwfilter.c conf/domain_nwfilter.h	\
diff --git a/src/conf/domain_capabilities.c b/src/conf/domain_capabilities.c
new file mode 100644
index 0000000..df190eb
--- /dev/null
+++ b/src/conf/domain_capabilities.c
@@ -0,0 +1,254 @@
+/*
+ * domain_capabilities.c: domain capabilities XML processing
+ *
+ * Copyright (C) 2014 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Author: Michal Privoznik <mprivozn at redhat.com>
+ */
+
+#include <config.h>
+
+#include "domain_capabilities.h"
+#include "domain_conf.h"
+#include "viralloc.h"
+#include "virstring.h"
+
+#define VIR_FROM_THIS VIR_FROM_CAPABILITIES
+
+static virClassPtr virDomainCapsClass;
+
+static void virDomainCapsDispose(void *obj);
+
+static int virDomainCapsOnceInit(void)
+{
+    if (!(virDomainCapsClass = virClassNew(virClassForObjectLockable(),
+                                           "virDomainCapsClass",
+                                           sizeof(virDomainCaps),
+                                           virDomainCapsDispose)))
+        return -1;
+    return 0;
+}
+
+
+VIR_ONCE_GLOBAL_INIT(virDomainCaps)
+
+
+static void
+virDomainCapsDispose(void *obj)
+{
+    virDomainCapsPtr caps = obj;
+
+    VIR_FREE(caps->path);
+    VIR_FREE(caps->machine);
+}
+
+
+virDomainCapsPtr
+virDomainCapsNew(const char *path,
+                 const char *machine,
+                 virArch arch,
+                 virDomainVirtType virttype)
+{
+    virDomainCapsPtr caps = NULL;
+
+    if (virDomainCapsInitialize() < 0)
+        return NULL;
+
+    if (!(caps = virObjectLockableNew(virDomainCapsClass)))
+        return NULL;
+
+    if (VIR_STRDUP(caps->path, path) < 0 ||
+        VIR_STRDUP(caps->machine, machine) < 0)
+        goto error;
+    caps->arch = arch;
+    caps->virttype = virttype;
+
+    return caps;
+ error:
+    virObjectUnref(caps);
+    return NULL;
+}
+
+
+int
+virDomainCapsEnumSet(virDomainCapsEnumPtr capsEnum,
+                     const char *capsEnumName,
+                     size_t nvalues,
+                     unsigned int *values)
+{
+    int ret = -1;
+    size_t i;
+
+    for (i = 0; i < nvalues; i++) {
+        unsigned int val = 1 << values[i];
+
+        if (!val) {
+            /* Integer overflow */
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("integer overflow on %s. Please contact the "
+                             "libvirt development team at libvir-list at redhat.com"),
+                           capsEnumName);
+            goto cleanup;
+        }
+
+        capsEnum->values |= val;
+    }
+
+    ret = 0;
+ cleanup:
+    return ret;
+}
+
+
+void
+virDomainCapsEnumClear(virDomainCapsEnumPtr capsEnum)
+{
+    capsEnum->values = 0;
+}
+
+
+static int
+virDomainCapsEnumFormat(virBufferPtr buf,
+                        virDomainCapsEnumPtr capsEnum,
+                        const char *capsEnumName,
+                        virDomainCapsValToStr valToStr)
+{
+    int ret = -1;
+    size_t i;
+
+    virBufferAsprintf(buf, "<enum name='%s'", capsEnumName);
+    if (!capsEnum->values) {
+        virBufferAddLit(buf, "/>\n");
+        ret = 0;
+        goto cleanup;
+    }
+    virBufferAddLit(buf, ">\n");
+    virBufferAdjustIndent(buf, 2);
+
+    for (i = 0; i < sizeof(capsEnum->values) * CHAR_BIT; i++) {
+        const char *val;
+
+        if (!(capsEnum->values & (1 << i)))
+            continue;
+
+        if ((val = (valToStr)(i)))
+            virBufferAsprintf(buf, "<value>%s</value>\n", val);
+    }
+    virBufferAdjustIndent(buf, -2);
+    virBufferAddLit(buf, "</enum>\n");
+
+    ret = 0;
+ cleanup:
+    return ret;
+}
+
+#define FORMAT_PROLOGUE(item)                                       \
+    do {                                                            \
+        virBufferAsprintf(buf, "<" #item " supported='%s'%s\n",     \
+                          item->device.supported ? "yes" : "no",    \
+                          item->device.supported ? ">" : "/>");     \
+        if (!item->device.supported)                                \
+            return;                                                 \
+        virBufferAdjustIndent(buf, 2);                              \
+    } while (0)
+
+#define FORMAT_EPILOGUE(item)                                       \
+    do {                                                            \
+        virBufferAdjustIndent(buf, -2);                             \
+        virBufferAddLit(buf, "</" #item ">\n");                     \
+    } while (0)
+
+#define ENUM_PROCESS(master, capsEnum, valToStr)                    \
+    do {                                                            \
+        virDomainCapsEnumFormat(buf, &master->capsEnum,             \
+                                #capsEnum, valToStr);               \
+    } while (0)
+
+static void
+virDomainCapsDeviceDiskFormat(virBufferPtr buf,
+                              virDomainCapsDeviceDiskPtr const disk)
+{
+    FORMAT_PROLOGUE(disk);
+
+    ENUM_PROCESS(disk, diskDevice, virDomainDiskDeviceTypeToString);
+    ENUM_PROCESS(disk, bus, virDomainDiskBusTypeToString);
+
+    FORMAT_EPILOGUE(disk);
+}
+
+
+static void
+virDomainCapsDeviceHostdevFormat(virBufferPtr buf,
+                                 virDomainCapsDeviceHostdevPtr const hostdev)
+{
+    FORMAT_PROLOGUE(hostdev);
+
+    ENUM_PROCESS(hostdev, mode, virDomainHostdevModeTypeToString);
+    ENUM_PROCESS(hostdev, startupPolicy, virDomainStartupPolicyTypeToString);
+    ENUM_PROCESS(hostdev, subsysType, virDomainHostdevSubsysTypeToString);
+    ENUM_PROCESS(hostdev, capsType, virDomainHostdevCapsTypeToString);
+    ENUM_PROCESS(hostdev, pciBackend, virDomainHostdevSubsysPCIBackendTypeToString);
+
+    FORMAT_EPILOGUE(hostdev);
+}
+
+
+static int
+virDomainCapsFormatInternal(virBufferPtr buf,
+                            virDomainCapsPtr const caps)
+{
+    const char *virttype_str = virDomainVirtTypeToString(caps->virttype);
+    const char *arch_str = virArchToString(caps->arch);
+
+    virBufferAddLit(buf, "<domainCapabilities>\n");
+    virBufferAdjustIndent(buf, 2);
+
+    virBufferAsprintf(buf, "<path>%s</path>\n", caps->path);
+    virBufferAsprintf(buf, "<domain>%s</domain>\n", virttype_str);
+    virBufferAsprintf(buf, "<machine>%s</machine>\n", caps->machine);
+    virBufferAsprintf(buf, "<arch>%s</arch>\n", arch_str);
+
+    if (caps->maxvcpus)
+        virBufferAsprintf(buf, "<vcpu max='%d'/>\n", caps->maxvcpus);
+
+    virBufferAddLit(buf, "<devices>\n");
+    virBufferAdjustIndent(buf, 2);
+
+    virDomainCapsDeviceDiskFormat(buf, &caps->disk);
+    virDomainCapsDeviceHostdevFormat(buf, &caps->hostdev);
+
+    virBufferAdjustIndent(buf, -2);
+    virBufferAddLit(buf, "</devices>\n");
+
+    virBufferAdjustIndent(buf, -2);
+    virBufferAddLit(buf, "</domainCapabilities>\n");
+    return 0;
+}
+
+
+char *
+virDomainCapsFormat(virDomainCapsPtr const caps)
+{
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
+
+    if (virDomainCapsFormatInternal(&buf, caps) < 0) {
+        virBufferFreeAndReset(&buf);
+        return NULL;
+    }
+
+    return virBufferContentAndReset(&buf);
+}
diff --git a/src/conf/domain_capabilities.h b/src/conf/domain_capabilities.h
new file mode 100644
index 0000000..731e66f
--- /dev/null
+++ b/src/conf/domain_capabilities.h
@@ -0,0 +1,103 @@
+/*
+ * domain_capabilities.h: domain capabilities XML processing
+ *
+ * Copyright (C) 2014 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Author: Michal Privoznik <mprivozn at redhat.com>
+ */
+
+#ifndef __DOMAIN_CAPABILITIES_H__
+# define __DOMAIN_CAPABILITIES_H__
+
+# include "internal.h"
+# include "domain_conf.h"
+
+typedef const char * (*virDomainCapsValToStr)(int value);
+
+typedef struct _virDomainCaps virDomainCaps;
+typedef virDomainCaps *virDomainCapsPtr;
+
+typedef struct _virDomainCapsEnum virDomainCapsEnum;
+typedef virDomainCapsEnum *virDomainCapsEnumPtr;
+struct _virDomainCapsEnum {
+    unsigned int values; /* Bitmask of values supported in the corresponding enum */
+};
+
+typedef struct _virDomainCapsDevice virDomainCapsDevice;
+typedef virDomainCapsDevice *virDomainCapsDevicePtr;
+struct _virDomainCapsDevice {
+    bool supported; /* true if <devtype> is supported by hypervisor */
+};
+
+typedef struct _virDomainCapsDeviceDisk virDomainCapsDeviceDisk;
+typedef virDomainCapsDeviceDisk *virDomainCapsDeviceDiskPtr;
+struct _virDomainCapsDeviceDisk {
+    virDomainCapsDevice device;
+    virDomainCapsEnum diskDevice;   /* Info about virDomainDiskDevice enum values */
+    virDomainCapsEnum bus;          /* Info about virDomainDiskBus enum values */
+    /* add new fields here */
+};
+
+typedef struct _virDomainCapsDeviceHostdev virDomainCapsDeviceHostdev;
+typedef virDomainCapsDeviceHostdev *virDomainCapsDeviceHostdevPtr;
+struct _virDomainCapsDeviceHostdev {
+    virDomainCapsDevice device;
+    virDomainCapsEnum mode;             /* Info about virDomainHostdevMode */
+    virDomainCapsEnum startupPolicy;    /* Info about virDomainStartupPolicy */
+    virDomainCapsEnum subsysType;       /* Info about virDomainHostdevSubsysType */
+    virDomainCapsEnum capsType;         /* Info about virDomainHostdevCapsType */
+    virDomainCapsEnum pciBackend;       /* Info about virDomainHostdevSubsysPCIBackendType */
+    /* add new fields here */
+};
+
+struct _virDomainCaps {
+    virObjectLockable parent;
+
+    char *path;                     /* path to emulator binary */
+    virDomainVirtType virttype;     /* virtualization type */
+    char *machine;                  /* machine type */
+    virArch arch;                   /* domain architecture */
+
+    /* Some machine specific info */
+    int maxvcpus;
+
+    virDomainCapsDeviceDisk disk;
+    virDomainCapsDeviceHostdev hostdev;
+    /* add new domain devices here */
+};
+
+virDomainCapsPtr virDomainCapsNew(const char *path,
+                                  const char *machine,
+                                  virArch arch,
+                                  virDomainVirtType virttype);
+
+# define VIR_DOMAIN_CAPS_ENUM_SET(capsEnum, ...)            \
+    do {                                                    \
+        unsigned int __values[] = {__VA_ARGS__};            \
+        size_t __nvalues = ARRAY_CARDINALITY(__values);     \
+        virDomainCapsEnumSet(&(capsEnum), #capsEnum,        \
+                             __nvalues, __values);          \
+    } while (0)
+
+int virDomainCapsEnumSet(virDomainCapsEnumPtr capsEnum,
+                         const char *capsEnumName,
+                         size_t nvalues,
+                         unsigned int *values);
+void virDomainCapsEnumClear(virDomainCapsEnumPtr capsEnum);
+
+char * virDomainCapsFormat(virDomainCapsPtr const caps);
+#endif /* __DOMAIN_CAPABILITIES_H__ */
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 1e1dd84..40585bd 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -130,6 +130,13 @@ virDomainAuditStop;
 virDomainAuditVcpu;
 
 
+# conf/domain_capabilities.h
+virDomainCapsEnumClear;
+virDomainCapsEnumSet;
+virDomainCapsFormat;
+virDomainCapsNew;
+
+
 # conf/domain_conf.h
 virBlkioDeviceArrayClear;
 virDiskNameToBusDeviceIndex;
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 025b847..97af0d9 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -74,6 +74,8 @@ EXTRA_DIST =		\
 	commanddata \
 	confdata \
 	cputestdata \
+	domaincapsschemadata \
+	domaincapsschematest \
 	domainconfdata \
 	domainschemadata \
 	domainschematest \
@@ -167,6 +169,7 @@ test_programs = virshtest sockettest \
 	virnetdevbandwidthtest \
 	virkmodtest \
 	vircapstest \
+	domaincapstest \
 	domainconftest \
 	virhostdevtest \
 	vircaps2xmltest \
@@ -318,6 +321,7 @@ test_scripts = \
 	networkschematest \
 	storagepoolschematest \
 	storagevolschematest \
+	domaincapsschematest \
 	domainschematest \
 	nodedevschematest \
 	nwfilterschematest \
@@ -827,6 +831,10 @@ vircaps2xmltest_SOURCES = \
 	vircaps2xmltest.c testutils.h testutils.c
 vircaps2xmltest_LDADD = $(LDADDS)
 
+domaincapstest_SOURCES = \
+	domaincapstest.c testutils.h testutils.c
+domaincapstest_LDADD = $(LDADDS)
+
 if WITH_LIBVIRTD
 libvirtdconftest_SOURCES = \
 	libvirtdconftest.c testutils.h testutils.c \
diff --git a/tests/domaincapsschemadata/domaincaps-basic.xml b/tests/domaincapsschemadata/domaincaps-basic.xml
new file mode 100644
index 0000000..9963519
--- /dev/null
+++ b/tests/domaincapsschemadata/domaincaps-basic.xml
@@ -0,0 +1,10 @@
+<domainCapabilities>
+  <path>/bin/emulatorbin</path>
+  <domain>uml</domain>
+  <machine>my-machine-type</machine>
+  <arch>x86_64</arch>
+  <devices>
+    <disk supported='no'/>
+    <hostdev supported='no'/>
+  </devices>
+</domainCapabilities>
diff --git a/tests/domaincapsschemadata/domaincaps-full.xml b/tests/domaincapsschemadata/domaincaps-full.xml
new file mode 100644
index 0000000..58dd4cb
--- /dev/null
+++ b/tests/domaincapsschemadata/domaincaps-full.xml
@@ -0,0 +1,56 @@
+<domainCapabilities>
+  <path>/bin/emulatorbin</path>
+  <domain>kvm</domain>
+  <machine>my-machine-type</machine>
+  <arch>x86_64</arch>
+  <vcpu max='255'/>
+  <devices>
+    <disk supported='yes'>
+      <enum name='diskDevice'>
+        <value>disk</value>
+        <value>cdrom</value>
+        <value>floppy</value>
+        <value>lun</value>
+      </enum>
+      <enum name='bus'>
+        <value>ide</value>
+        <value>fdc</value>
+        <value>scsi</value>
+        <value>virtio</value>
+        <value>xen</value>
+        <value>usb</value>
+        <value>uml</value>
+        <value>sata</value>
+        <value>sd</value>
+      </enum>
+    </disk>
+    <hostdev supported='yes'>
+      <enum name='mode'>
+        <value>subsystem</value>
+        <value>capabilities</value>
+      </enum>
+      <enum name='startupPolicy'>
+        <value>default</value>
+        <value>mandatory</value>
+        <value>requisite</value>
+        <value>optional</value>
+      </enum>
+      <enum name='subsysType'>
+        <value>usb</value>
+        <value>pci</value>
+        <value>scsi</value>
+      </enum>
+      <enum name='capsType'>
+        <value>storage</value>
+        <value>misc</value>
+        <value>net</value>
+      </enum>
+      <enum name='pciBackend'>
+        <value>default</value>
+        <value>kvm</value>
+        <value>vfio</value>
+        <value>xen</value>
+      </enum>
+    </hostdev>
+  </devices>
+</domainCapabilities>
diff --git a/tests/domaincapsschematest b/tests/domaincapsschematest
new file mode 100755
index 0000000..9baf44a
--- /dev/null
+++ b/tests/domaincapsschematest
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+: ${srcdir=.}
+. $srcdir/test-lib.sh
+. $abs_srcdir/schematestutils.sh
+
+DIRS=""
+DIRS="$DIRS domaincapsschemadata"
+SCHEMA="domaincaps.rng"
+
+check_schema "$DIRS" "$SCHEMA"
diff --git a/tests/domaincapstest.c b/tests/domaincapstest.c
new file mode 100644
index 0000000..6cdd086
--- /dev/null
+++ b/tests/domaincapstest.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) Red Hat, Inc. 2014
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *      Michal Privoznik <mprivozn at redhat.com>
+ */
+
+#include <config.h>
+#include <stdlib.h>
+
+#include "testutils.h"
+#include "domain_capabilities.h"
+
+
+#define VIR_FROM_THIS VIR_FROM_NONE
+
+typedef void (*virDomainCapsFill)(virDomainCapsPtr domCaps,
+                                  void *opaque);
+
+#define SET_ALL_BITS(x) \
+    memset(&(x.values), 0xff, sizeof(x.values))
+
+static void
+fillAll(virDomainCapsPtr domCaps,
+        void *opaque ATTRIBUTE_UNUSED)
+{
+    virDomainCapsDeviceDiskPtr disk = &domCaps->disk;
+    virDomainCapsDeviceHostdevPtr hostdev = &domCaps->hostdev;
+    domCaps->maxvcpus = 255;
+
+    disk->device.supported = true;
+    SET_ALL_BITS(disk->diskDevice);
+    SET_ALL_BITS(disk->bus);
+
+    hostdev->device.supported = true;
+    SET_ALL_BITS(hostdev->mode);
+    SET_ALL_BITS(hostdev->startupPolicy);
+    SET_ALL_BITS(hostdev->subsysType);
+    SET_ALL_BITS(hostdev->capsType);
+    SET_ALL_BITS(hostdev->pciBackend);
+}
+
+static virDomainCapsPtr
+buildVirDomainCaps(const char *emulatorbin,
+                   const char *machine,
+                   virArch arch,
+                   virDomainVirtType type,
+                   virDomainCapsFill fillFunc,
+                   void *opaque)
+{
+    virDomainCapsPtr domCaps;
+
+    if (!(domCaps = virDomainCapsNew(emulatorbin, machine, arch, type)))
+        goto cleanup;
+
+    if (fillFunc)
+        fillFunc(domCaps, opaque);
+
+ cleanup:
+    return domCaps;
+}
+
+struct test_virDomainCapsFormatData {
+    const char *filename;
+    const char *emulatorbin;
+    const char *machine;
+    virArch arch;
+    virDomainVirtType type;
+    virDomainCapsFill fillFunc;
+    void *opaque;
+};
+
+static int
+test_virDomainCapsFormat(const void *opaque)
+{
+    struct test_virDomainCapsFormatData *data =
+        (struct test_virDomainCapsFormatData *) opaque;
+    virDomainCapsPtr domCaps = NULL;
+    char *path = NULL;
+    char *domCapsXML = NULL;
+    char *domCapsFromFile = NULL;
+    int ret = -1;
+
+    if (virAsprintf(&path, "%s/domaincapsschemadata/domaincaps-%s.xml",
+                    abs_srcdir, data->filename) < 0)
+        goto cleanup;
+
+    if (virFileReadAll(path, 8192, &domCapsFromFile) < 0)
+        goto cleanup;
+
+    if (!(domCaps = buildVirDomainCaps(data->emulatorbin, data->machine,
+                                       data->arch, data->type,
+                                       data->fillFunc, data->opaque)))
+        goto cleanup;
+
+    if (!(domCapsXML = virDomainCapsFormat(domCaps)))
+        goto cleanup;
+
+    if (STRNEQ(domCapsFromFile, domCapsXML)) {
+        virtTestDifference(stderr, domCapsFromFile, domCapsXML);
+        goto cleanup;
+    }
+
+    ret = 0;
+ cleanup:
+    VIR_FREE(domCapsFromFile);
+    VIR_FREE(domCapsXML);
+    VIR_FREE(path);
+    virObjectUnref(domCaps);
+    return ret;
+}
+
+static int
+mymain(void)
+{
+    int ret = 0;
+
+#define DO_TEST(Filename, Emulatorbin, Machine, Arch, Type, ...)                    \
+    do {                                                                            \
+        struct test_virDomainCapsFormatData data = {.filename = Filename,           \
+            .emulatorbin = Emulatorbin, .machine = Machine, .arch = Arch,           \
+            .type = Type, __VA_ARGS__};                                             \
+        if (virtTestRun(Filename, test_virDomainCapsFormat, &data) < 0)             \
+            ret = -1;                                                               \
+    } while (0)
+
+    DO_TEST("basic", "/bin/emulatorbin", "my-machine-type",
+            VIR_ARCH_X86_64, VIR_DOMAIN_VIRT_UML);
+    DO_TEST("full", "/bin/emulatorbin", "my-machine-type",
+            VIR_ARCH_X86_64, VIR_DOMAIN_VIRT_KVM, .fillFunc = fillAll);
+
+    return ret;
+}
+
+VIRT_TEST_MAIN(mymain)
-- 
1.8.5.5




More information about the libvir-list mailing list