virDomainVideoTypeToString;
virDomainVirtioEventIdxTypeFromString;
virDomainVirtioEventIdxTypeToString;
+virDomainVirtTypeFromString;
virDomainVirtTypeToString;
virDomainWatchdogActionTypeFromString;
virDomainWatchdogActionTypeToString;
#include "virnodesuspend.h"
#include "qemu_monitor.h"
#include "virstring.h"
+#include "qemu_hostdev.h"
#include <fcntl.h>
#include <sys/stat.h>
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);
+}
# 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 {
virQEMUCapsPtr kvmbinCaps,
virArch guestarch);
+void virQEMUCapsFillDomainCaps(virDomainCapsPtr domCaps,
+ virQEMUCapsPtr qemuCaps);
+
#endif /* __QEMU_CAPABILITIES_H__*/
#include "viraccessapicheckqemu.h"
#include "storage/storage_driver.h"
#include "virhostdev.h"
+#include "domain_capabilities.h"
#define VIR_FROM_THIS VIR_FROM_QEMU
}
+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,
.domainGetTime = qemuDomainGetTime, /* 1.2.5 */
.domainSetTime = qemuDomainSetTime, /* 1.2.5 */
.nodeGetFreePages = qemuNodeGetFreePages, /* 1.2.6 */
+ .connectGetDomainCapabilities = qemuConnectGetDomainCapabilities, /* 1.2.7 */
};
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 \
--- /dev/null
+<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>
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,
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;
}