mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-01-24 06:03:52 +03:00
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:
parent
cb01d2b5b1
commit
94e3f23e8a
@ -439,6 +439,7 @@ virDomainVideoTypeFromString;
|
||||
virDomainVideoTypeToString;
|
||||
virDomainVirtioEventIdxTypeFromString;
|
||||
virDomainVirtioEventIdxTypeToString;
|
||||
virDomainVirtTypeFromString;
|
||||
virDomainVirtTypeToString;
|
||||
virDomainWatchdogActionTypeFromString;
|
||||
virDomainWatchdogActionTypeToString;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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__*/
|
||||
|
@ -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 */
|
||||
};
|
||||
|
||||
|
||||
|
@ -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 \
|
||||
|
45
tests/domaincapsschemadata/domaincaps-qemu_1.6.50-1.xml
Normal file
45
tests/domaincapsschemadata/domaincaps-qemu_1.6.50-1.xml
Normal 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>
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user