]> xenbits.xensource.com Git - people/liuw/libxenctrl-split/libvirt.git/commitdiff
qemu: Implement virConnectGetDomainCapabilities
authorMichal Privoznik <mprivozn@redhat.com>
Wed, 25 Jun 2014 16:39:29 +0000 (18:39 +0200)
committerMichal Privoznik <mprivozn@redhat.com>
Thu, 3 Jul 2014 10:22:38 +0000 (12:22 +0200)
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@redhat.com>
src/libvirt_private.syms
src/qemu/qemu_capabilities.c
src/qemu/qemu_capabilities.h
src/qemu/qemu_driver.c
tests/Makefile.am
tests/domaincapsschemadata/domaincaps-qemu_1.6.50-1.xml [new file with mode: 0644]
tests/domaincapstest.c

index e3164403df8f571efc5cbc668ab2445c911734ba..11d132e952cae3bbe2569135398a5af78dfe3d1d 100644 (file)
@@ -439,6 +439,7 @@ virDomainVideoTypeFromString;
 virDomainVideoTypeToString;
 virDomainVirtioEventIdxTypeFromString;
 virDomainVirtioEventIdxTypeToString;
+virDomainVirtTypeFromString;
 virDomainVirtTypeToString;
 virDomainWatchdogActionTypeFromString;
 virDomainWatchdogActionTypeToString;
index ac6b5c82c2f9d6f9ad55806e0f4cbfd96b309eb7..934a7b02b6e59b121666369441c510ffae9cc261 100644 (file)
@@ -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->mode,
+                             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);
+}
index d0a1092ada1609c6ed9ac9a44bcc4c0031da146d..17be405b1c1d89fa52fe87738709df294eb321a7 100644 (file)
@@ -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__*/
index 2a01c9ca6edd8e42a5001a0f98e98217b9ae4f5b..1983cef541b0247c581b38adbb9656d5eedee4a2 100644 (file)
@@ -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
 
@@ -16885,6 +16886,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,
@@ -17080,6 +17180,7 @@ static virDriver qemuDriver = {
     .domainGetTime = qemuDomainGetTime, /* 1.2.5 */
     .domainSetTime = qemuDomainSetTime, /* 1.2.5 */
     .nodeGetFreePages = qemuNodeGetFreePages, /* 1.2.6 */
+    .connectGetDomainCapabilities = qemuConnectGetDomainCapabilities, /* 1.2.7 */
 };
 
 
index 97af0d9d3729d640da2ae87f40782c66da54ab96..a262c7ba7f7964a4f6fee201e0f2d0fc830b1d42 100644 (file)
@@ -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 (file)
index 0000000..8b63993
--- /dev/null
@@ -0,0 +1,45 @@
+<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>
+      </enum>
+    </disk>
+    <hostdev supported='yes'>
+      <enum name='mode'>
+        <value>subsystem</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'/>
+      <enum name='pciBackend'>
+        <value>default</value>
+        <value>kvm</value>
+        <value>vfio</value>
+      </enum>
+    </hostdev>
+  </devices>
+</domainCapabilities>
index 6cdd086dcb2f91191e367c8c69f333515d68b769..78197e2350ec249cbb54f311048f66077fca8cca 100644 (file)
@@ -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;
 }