[libvirt] [PATCH for 1.2.7 8/8] qemu: Implement virConnectGetDomainCapabilities

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


So far only information on disks and host devices are exposed in the
capabilities XML. Well, at least something. Even a new test is
introduced. The qemu capabilities are stolen from already existing
qemucapabilities test. There's one tricky point though. Functions that
checks host's KVM and VFIO capabilities, are impossible to mock
currently. So in the test, we are setting the capabilities by hand.

Signed-off-by: Michal Privoznik <mprivozn at redhat.com>
---
 src/libvirt_private.syms                           |   1 +
 src/qemu/qemu_capabilities.c                       |  90 ++++++++++++++++++
 src/qemu/qemu_capabilities.h                       |   4 +
 src/qemu/qemu_driver.c                             | 101 +++++++++++++++++++++
 tests/Makefile.am                                  |   5 +
 .../domaincaps-qemu_1.6.50-1.xml                   |  44 +++++++++
 tests/domaincapstest.c                             |  45 +++++++++
 7 files changed, 290 insertions(+)
 create mode 100644 tests/domaincapsschemadata/domaincaps-qemu_1.6.50-1.xml

diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 40585bd..a1dc8ae 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -439,6 +439,7 @@ virDomainVideoTypeFromString;
 virDomainVideoTypeToString;
 virDomainVirtioEventIdxTypeFromString;
 virDomainVirtioEventIdxTypeToString;
+virDomainVirtTypeFromString;
 virDomainVirtTypeToString;
 virDomainWatchdogActionTypeFromString;
 virDomainWatchdogActionTypeToString;
diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c
index e4336e1..9fc58ff 100644
--- a/src/qemu/qemu_capabilities.c
+++ b/src/qemu/qemu_capabilities.c
@@ -39,6 +39,7 @@
 #include "virnodesuspend.h"
 #include "qemu_monitor.h"
 #include "virstring.h"
+#include "qemu_hostdev.h"
 
 #include <fcntl.h>
 #include <sys/stat.h>
@@ -3565,3 +3566,92 @@ virQEMUCapsGetDefaultMachine(virQEMUCapsPtr qemuCaps)
         return NULL;
     return qemuCaps->machineTypes[0];
 }
+
+
+static void
+virQEMUCapsFillDomainDeviceDiskCaps(virQEMUCapsPtr qemuCaps,
+                                    virDomainCapsDeviceDiskPtr disk)
+{
+    disk->device.supported = true;
+    /* QEMU supports all of these */
+    VIR_DOMAIN_CAPS_ENUM_SET(disk->diskDevice,
+                             VIR_DOMAIN_DISK_DEVICE_DISK,
+                             VIR_DOMAIN_DISK_DEVICE_CDROM,
+                             VIR_DOMAIN_DISK_DEVICE_FLOPPY);
+
+    if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_BLK_SG_IO))
+        VIR_DOMAIN_CAPS_ENUM_SET(disk->diskDevice, VIR_DOMAIN_DISK_DEVICE_LUN);
+
+    VIR_DOMAIN_CAPS_ENUM_SET(disk->bus,
+                             VIR_DOMAIN_DISK_BUS_IDE,
+                             VIR_DOMAIN_DISK_BUS_FDC,
+                             VIR_DOMAIN_DISK_BUS_SCSI,
+                             VIR_DOMAIN_DISK_BUS_VIRTIO,
+                             VIR_DOMAIN_DISK_BUS_SD);
+
+    if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_USB_STORAGE))
+        VIR_DOMAIN_CAPS_ENUM_SET(disk->bus, VIR_DOMAIN_DISK_BUS_USB);
+}
+
+
+static void
+virQEMUCapsFillDomainDeviceHostdevCaps(virQEMUCapsPtr qemuCaps,
+                                       virDomainCapsDeviceHostdevPtr hostdev)
+{
+    bool supportsPassthroughKVM = qemuHostdevHostSupportsPassthroughLegacy();
+    bool supportsPassthroughVFIO = qemuHostdevHostSupportsPassthroughVFIO();
+
+    hostdev->device.supported = true;
+    /* VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES is for containers only */
+    VIR_DOMAIN_CAPS_ENUM_SET(hostdev->subsysType,
+                             VIR_DOMAIN_HOSTDEV_MODE_SUBSYS);
+
+    VIR_DOMAIN_CAPS_ENUM_SET(hostdev->startupPolicy,
+                             VIR_DOMAIN_STARTUP_POLICY_DEFAULT,
+                             VIR_DOMAIN_STARTUP_POLICY_MANDATORY,
+                             VIR_DOMAIN_STARTUP_POLICY_REQUISITE,
+                             VIR_DOMAIN_STARTUP_POLICY_OPTIONAL);
+
+    VIR_DOMAIN_CAPS_ENUM_SET(hostdev->subsysType,
+                             VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB,
+                             VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI);
+    if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE) &&
+        virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE) &&
+        virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_SCSI_GENERIC))
+        VIR_DOMAIN_CAPS_ENUM_SET(hostdev->subsysType,
+                                 VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI);
+
+    /* No virDomainHostdevCapsType for QEMU */
+    virDomainCapsEnumClear(&hostdev->capsType);
+
+    virDomainCapsEnumClear(&hostdev->pciBackend);
+    if (supportsPassthroughVFIO &&
+        virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VFIO_PCI)) {
+        VIR_DOMAIN_CAPS_ENUM_SET(hostdev->pciBackend,
+                                 VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT,
+                                 VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO);
+    }
+
+    if (supportsPassthroughKVM &&
+        (virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCIDEVICE) ||
+         virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE))) {
+        VIR_DOMAIN_CAPS_ENUM_SET(hostdev->pciBackend,
+                                 VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT,
+                                 VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM);
+    }
+}
+
+
+void
+virQEMUCapsFillDomainCaps(virDomainCapsPtr domCaps,
+                          virQEMUCapsPtr qemuCaps)
+{
+    virDomainCapsDeviceDiskPtr disk = &domCaps->disk;
+    virDomainCapsDeviceHostdevPtr hostdev = &domCaps->hostdev;
+    int maxvcpus = virQEMUCapsGetMachineMaxCpus(qemuCaps, domCaps->machine);
+
+    domCaps->maxvcpus = maxvcpus;
+
+    virQEMUCapsFillDomainDeviceDiskCaps(qemuCaps, disk);
+    virQEMUCapsFillDomainDeviceHostdevCaps(qemuCaps, hostdev);
+}
diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h
index d0a1092..17be405 100644
--- a/src/qemu/qemu_capabilities.h
+++ b/src/qemu/qemu_capabilities.h
@@ -28,6 +28,7 @@
 # include "capabilities.h"
 # include "vircommand.h"
 # include "qemu_monitor.h"
+# include "domain_capabilities.h"
 
 /* Internal flags to keep track of qemu command line capabilities */
 typedef enum {
@@ -314,4 +315,7 @@ int virQEMUCapsInitGuestFromBinary(virCapsPtr caps,
                                    virQEMUCapsPtr kvmbinCaps,
                                    virArch guestarch);
 
+void virQEMUCapsFillDomainCaps(virDomainCapsPtr domCaps,
+                               virQEMUCapsPtr qemuCaps);
+
 #endif /* __QEMU_CAPABILITIES_H__*/
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 775f6ab..f5d28d5 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -95,6 +95,7 @@
 #include "viraccessapicheckqemu.h"
 #include "storage/storage_driver.h"
 #include "virhostdev.h"
+#include "domain_capabilities.h"
 
 #define VIR_FROM_THIS VIR_FROM_QEMU
 
@@ -16903,6 +16904,105 @@ qemuNodeGetFreePages(virConnectPtr conn,
 }
 
 
+static char *
+qemuConnectGetDomainCapabilities(virConnectPtr conn,
+                                 const char *emulatorbin,
+                                 const char *arch_str,
+                                 const char *machine,
+                                 const char *virttype_str,
+                                 unsigned int flags)
+{
+    char *ret = NULL;
+    virQEMUDriverPtr driver = conn->privateData;
+    virQEMUCapsPtr qemuCaps = NULL;
+    int virttype; /* virDomainVirtType */
+    virDomainCapsPtr domCaps = NULL;
+    int arch = VIR_ARCH_NONE; /* virArch */
+
+    virCheckFlags(0, ret);
+    virCheckNonNullArgReturn(virttype_str, ret);
+
+    if (virConnectGetDomainCapabilitiesEnsureACL(conn) < 0)
+        return ret;
+
+    if ((virttype = virDomainVirtTypeFromString(virttype_str)) < 0) {
+        virReportError(VIR_ERR_INVALID_ARG,
+                       _("unknown virttype: %s"),
+                       virttype_str);
+        goto cleanup;
+    }
+
+    if (arch_str && (arch = virArchFromString(arch_str)) == VIR_ARCH_NONE) {
+        virReportError(VIR_ERR_INVALID_ARG,
+                       _("unknown architecture: %s"),
+                       arch_str);
+        goto cleanup;
+    }
+
+    if (emulatorbin) {
+        virArch arch_from_caps;
+
+        if (!(qemuCaps = virQEMUCapsCacheLookup(driver->qemuCapsCache,
+                                                emulatorbin)))
+            goto cleanup;
+
+        arch_from_caps = virQEMUCapsGetArch(qemuCaps);
+
+        if (arch == VIR_ARCH_NONE)
+            arch = arch_from_caps;
+
+        if (arch_from_caps != arch) {
+            virReportError(VIR_ERR_INVALID_ARG,
+                           _("architecture from emulator '%s' doesn't "
+                             "match given architecture '%s'"),
+                           virArchToString(arch_from_caps),
+                           virArchToString(arch));
+            goto cleanup;
+        }
+    } else if (arch_str) {
+        if (!(qemuCaps = virQEMUCapsCacheLookupByArch(driver->qemuCapsCache,
+                                                      arch)))
+            goto cleanup;
+
+        if (!emulatorbin)
+            emulatorbin = virQEMUCapsGetBinary(qemuCaps);
+        /* Deliberately not checking if provided @emulatorbin matches @arch,
+         * since if @emulatorbin was specified the match has been checked a few
+         * lines above. */
+    } else {
+        virReportError(VIR_ERR_INVALID_ARG, "%s",
+                       _("at least one of emulatorbin or "
+                         "architecture fields must be present"));
+        goto cleanup;
+    }
+
+    if (machine) {
+        /* Turn @machine into canonical name */
+        machine = virQEMUCapsGetCanonicalMachine(qemuCaps, machine);
+
+        if (!virQEMUCapsIsMachineSupported(qemuCaps, machine)) {
+            virReportError(VIR_ERR_INVALID_ARG,
+                           _("the machine '%s' is not supported by emulator '%s'"),
+                           machine, emulatorbin);
+            goto cleanup;
+        }
+    } else {
+        machine = virQEMUCapsGetDefaultMachine(qemuCaps);
+    }
+
+    if (!(domCaps = virDomainCapsNew(emulatorbin, machine, arch, virttype)))
+        goto cleanup;
+
+    virQEMUCapsFillDomainCaps(domCaps, qemuCaps);
+
+    ret = virDomainCapsFormat(domCaps);
+ cleanup:
+    virObjectUnref(domCaps);
+    virObjectUnref(qemuCaps);
+    return ret;
+}
+
+
 static virDriver qemuDriver = {
     .no = VIR_DRV_QEMU,
     .name = QEMU_DRIVER_NAME,
@@ -17098,6 +17198,7 @@ static virDriver qemuDriver = {
     .domainGetTime = qemuDomainGetTime, /* 1.2.5 */
     .domainSetTime = qemuDomainSetTime, /* 1.2.5 */
     .nodeGetFreePages = qemuNodeGetFreePages, /* 1.2.6 */
+    .connectGetDomainCapabilities = qemuConnectGetDomainCapabilities, /* 1.2.7 */
 };
 
 
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 97af0d9..a262c7b 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -835,6 +835,11 @@ domaincapstest_SOURCES = \
 	domaincapstest.c testutils.h testutils.c
 domaincapstest_LDADD = $(LDADDS)
 
+if WITH_QEMU
+domaincapstest_SOURCES += testutilsqemu.c testutilsqemu.h
+domaincapstest_LDADD += $(qemu_LDADDS)
+endif WITH_QEMU
+
 if WITH_LIBVIRTD
 libvirtdconftest_SOURCES = \
 	libvirtdconftest.c testutils.h testutils.c \
diff --git a/tests/domaincapsschemadata/domaincaps-qemu_1.6.50-1.xml b/tests/domaincapsschemadata/domaincaps-qemu_1.6.50-1.xml
new file mode 100644
index 0000000..562e2f4
--- /dev/null
+++ b/tests/domaincapsschemadata/domaincaps-qemu_1.6.50-1.xml
@@ -0,0 +1,44 @@
+<domainCapabilities>
+  <path>/usr/bin/qemu-system-x86_64</path>
+  <domain>kvm</domain>
+  <machine>pc-1.2</machine>
+  <arch>x86_64</arch>
+  <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>usb</value>
+        <value>sd</value>
+      </enum>
+    </disk>
+    <hostdev supported='yes'>
+      <enum name='mode'/>
+      <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'/>
+      <enum name='pciBackend'>
+        <value>default</value>
+        <value>kvm</value>
+        <value>vfio</value>
+      </enum>
+    </hostdev>
+  </devices>
+</domainCapabilities>
diff --git a/tests/domaincapstest.c b/tests/domaincapstest.c
index 6cdd086..78197e2 100644
--- a/tests/domaincapstest.c
+++ b/tests/domaincapstest.c
@@ -54,6 +54,30 @@ fillAll(virDomainCapsPtr domCaps,
     SET_ALL_BITS(hostdev->pciBackend);
 }
 
+
+#ifdef WITH_QEMU
+# include "testutilsqemu.h"
+static void
+fillQemuCaps(virDomainCapsPtr domCaps,
+             void *opaque)
+{
+    virQEMUCapsPtr qemuCaps = (virQEMUCapsPtr) opaque;
+
+    virQEMUCapsFillDomainCaps(domCaps, qemuCaps);
+
+    /* The function above tries to query host's KVM & VFIO capabilities by
+     * calling qemuHostdevHostSupportsPassthroughLegacy() and
+     * qemuHostdevHostSupportsPassthroughVFIO() which, however, can't be
+     * successfully mocked as they are not exposed as internal APIs. Therefore,
+     * instead of mocking set the expected values here by hand. */
+    VIR_DOMAIN_CAPS_ENUM_SET(domCaps->hostdev.pciBackend,
+                             VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT,
+                             VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM,
+                             VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO);
+}
+#endif /* WITH_QEMU */
+
+
 static virDomainCapsPtr
 buildVirDomainCaps(const char *emulatorbin,
                    const char *machine,
@@ -143,6 +167,27 @@ mymain(void)
     DO_TEST("full", "/bin/emulatorbin", "my-machine-type",
             VIR_ARCH_X86_64, VIR_DOMAIN_VIRT_KVM, .fillFunc = fillAll);
 
+#ifdef WITH_QEMU
+
+# define DO_TEST_QEMU(Filename, QemuCapsFile, Emulatorbin, Machine, Arch, Type, ...)    \
+    do {                                                                                \
+        const char *capsPath = abs_srcdir "/qemucapabilitiesdata/" QemuCapsFile ".caps";    \
+        virQEMUCapsPtr qemuCaps = qemuTestParseCapabilities(capsPath);                  \
+        struct test_virDomainCapsFormatData data = {.filename = Filename,               \
+            .emulatorbin = Emulatorbin, .machine = Machine, .arch = Arch,               \
+            .type = Type, .fillFunc = fillQemuCaps, .opaque = qemuCaps};                \
+        if (!qemuCaps) {                                                                \
+            fprintf(stderr, "Unable to build qemu caps from %s\n", capsPath);           \
+            ret = -1;                                                                   \
+        } else if (virtTestRun(Filename, test_virDomainCapsFormat, &data) < 0)          \
+            ret = -1;                                                                   \
+    } while (0)
+
+    DO_TEST_QEMU("qemu_1.6.50-1", "caps_1.6.50-1", "/usr/bin/qemu-system-x86_64",
+                 "pc-1.2",  VIR_ARCH_X86_64, VIR_DOMAIN_VIRT_KVM);
+
+#endif /* WITH_QEMU */
+
     return ret;
 }
 
-- 
1.8.5.5




More information about the libvir-list mailing list