qemu: Implement virConnectGetDomainCapabilities

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>
This commit is contained in:
Michal Privoznik 2014-06-25 18:39:29 +02:00
parent cb01d2b5b1
commit 94e3f23e8a
7 changed files with 291 additions and 0 deletions

View File

@ -439,6 +439,7 @@ virDomainVideoTypeFromString;
virDomainVideoTypeToString;
virDomainVirtioEventIdxTypeFromString;
virDomainVirtioEventIdxTypeToString;
virDomainVirtTypeFromString;
virDomainVirtTypeToString;
virDomainWatchdogActionTypeFromString;
virDomainWatchdogActionTypeToString;

View 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);
}

View 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__*/

View 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 */
};

View 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 \

View File

@ -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>

View 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;
}