[libvirt] [PATCH for 1.2.7 0/8] Expose Expose IOMMU and VFIO host capabilities

After my RFC round, here are the actual patches. Michal Privoznik (8): Introduce domain_capabilities Introduce virConnectGetDomainCapabilities virsh: expose virConnectGetDomainCapabilities tests: Move qemu caps XML parsing into shared unit qemu_capabilities: Introduce virQEMUCapsCacheLookupByArch qemu_capabilities: Introduce virQEMUCapsIsMachineSupported qemu_capabilities: Introduce virQEMUCapsGetDefaultMachine qemu: Implement virConnectGetDomainCapabilities docs/formatdomaincaps.html.in | 200 ++++++++++++++++ docs/schemas/Makefile.am | 1 + docs/schemas/domaincaps.rng | 90 ++++++++ docs/sitemap.html.in | 4 + include/libvirt/libvirt.h.in | 7 + libvirt.spec.in | 1 + mingw-libvirt.spec.in | 2 + po/POTFILES.in | 1 + src/Makefile.am | 1 + src/conf/domain_capabilities.c | 254 +++++++++++++++++++++ src/conf/domain_capabilities.h | 103 +++++++++ src/driver.h | 9 + src/libvirt.c | 52 +++++ src/libvirt_private.syms | 8 + src/libvirt_public.syms | 5 + src/qemu/qemu_capabilities.c | 146 ++++++++++++ src/qemu/qemu_capabilities.h | 11 + src/qemu/qemu_driver.c | 101 ++++++++ src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 19 +- src/remote_protocol-structs | 11 + tests/Makefile.am | 13 ++ tests/domaincapsschemadata/domaincaps-basic.xml | 10 + tests/domaincapsschemadata/domaincaps-full.xml | 56 +++++ .../domaincaps-qemu_1.6.50-1.xml | 44 ++++ tests/domaincapsschematest | 11 + tests/domaincapstest.c | 194 ++++++++++++++++ tests/qemucapabilitiestest.c | 57 +---- tests/testutilsqemu.c | 49 ++++ tests/testutilsqemu.h | 3 + tools/virsh-host.c | 84 +++++++ tools/virsh.pod | 16 ++ 32 files changed, 1509 insertions(+), 55 deletions(-) create mode 100644 docs/formatdomaincaps.html.in create mode 100644 docs/schemas/domaincaps.rng create mode 100644 src/conf/domain_capabilities.c create mode 100644 src/conf/domain_capabilities.h create mode 100644 tests/domaincapsschemadata/domaincaps-basic.xml create mode 100644 tests/domaincapsschemadata/domaincaps-full.xml create mode 100644 tests/domaincapsschemadata/domaincaps-qemu_1.6.50-1.xml create mode 100755 tests/domaincapsschematest create mode 100644 tests/domaincapstest.c -- 1.8.5.5

This new module holds and formats capabilities for emulator. If you are about to create a new domain, you may want to know what is the host or hypervisor capable of. To make sure we don't regress on the XML, the formatting is not something left for each driver to implement, rather there's general format function. The domain capabilities is a lockable object (even though the locking is not necessary yet) which uses reference counter. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- docs/formatdomaincaps.html.in | 200 +++++++++++++++++++ docs/schemas/Makefile.am | 1 + docs/schemas/domaincaps.rng | 90 +++++++++ docs/sitemap.html.in | 4 + libvirt.spec.in | 1 + mingw-libvirt.spec.in | 2 + po/POTFILES.in | 1 + src/Makefile.am | 1 + src/conf/domain_capabilities.c | 254 ++++++++++++++++++++++++ src/conf/domain_capabilities.h | 103 ++++++++++ src/libvirt_private.syms | 7 + tests/Makefile.am | 8 + tests/domaincapsschemadata/domaincaps-basic.xml | 10 + tests/domaincapsschemadata/domaincaps-full.xml | 56 ++++++ tests/domaincapsschematest | 11 + tests/domaincapstest.c | 149 ++++++++++++++ 16 files changed, 898 insertions(+) create mode 100644 docs/formatdomaincaps.html.in create mode 100644 docs/schemas/domaincaps.rng create mode 100644 src/conf/domain_capabilities.c create mode 100644 src/conf/domain_capabilities.h create mode 100644 tests/domaincapsschemadata/domaincaps-basic.xml create mode 100644 tests/domaincapsschemadata/domaincaps-full.xml create mode 100755 tests/domaincapsschematest create mode 100644 tests/domaincapstest.c diff --git a/docs/formatdomaincaps.html.in b/docs/formatdomaincaps.html.in new file mode 100644 index 0000000..cfd61d9 --- /dev/null +++ b/docs/formatdomaincaps.html.in @@ -0,0 +1,200 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> + <body> + <h1>Domain capabilities XML format</h1> + + <ul id="toc"></ul> + + <h2><a name="Motivation">Motivation</a></h2> + + <p>Sometimes, when a new domain is to be created it may come handy to know + the capabilities of the hypervisor so the correct combination of devices and + drivers is used. For example, when management application is considering the + mode for a host device's passthrough there are several options depending not + only on host, but on hypervisor in question too. If the hypervisor is qemu + then it needs to be more recent to support VFIO, while legacy KVM is + achievable just fine with older one.</p> + + <p>The main difference between <a + href="formatcaps.html">virConnectGetCapabilities</a> and the emulator + capabilities API is, the former one aims more on the host capabilities (e.g. + NUMA topology, security models in effect, etc.) while the latter one + specializes on the hypervisor capabilities.</p> + + <h2><a name="elements">Element and attribute overview</a></h2> + + <p>The root element that emulator capability XML document starts with has + name <code>domainCapabilities</code>. It contains at least four direct + child elements:</p> + +<pre> +<domainCapabilities> + <path>/usr/bin/qemu-system-x86_64</path> + <domain>kvm</domain> + <machine>pc-i440fx-2.1</machine> + <arch>x86_64</arch> + ... +</domainCapabilities> +</pre> + <dl> + <dt>path</dt> + <dd>The full path to the emulator binary.</dd> + + <dt>domain</dt> + <dd>Describes the <a href="formatdomain.html#elements">virtualization + type</a> (or so called domain type).</dd> + + <dt>machine</dt> + <dd>The domain's <a href="formatdomain.html#elementsOSBIOS">machine + type</a>.</dd> + + <dt>arch</dt> + <dd>The domain's <a href="formatdomain.html#elementsOSBIOS"> + architecture</a>.</dd> + + </dl> + + <h3><a name="elementsCPUAllocation">CPU Allocation</a></h3> + +<pre> +<domainCapabilities> + ... + <vcpu max='255'/> + ... +</domainCapabilities> +</pre> + + <dl> + <dt>vcpu</dt> + <dd>The maximum number of supported virtual CPUs</dd> + </dl> + + <h3><a name="elementsDevices">Devices</a></h3> + + <p> + The final set of XML elements are all used to describe supported devices + and their capabilities. All devices occur as children of the main + <code>devices</code> element. + </p> + +<pre> +<domainCapabilities> + ... + <devices> + <disk supported='yes'> + <enum name='diskDevice'> + <value>disk</value> + <value>cdrom</value> + <value>floppy</value> + <value>lun</value> + </enum> + ... + </disk> + <hostdev supported='no'/> + </devices> +</domainCapabilities> +</pre> + + <p>All reported capabilities share the way that they are expressed. In the + example above we can see what disk devices are supported (<code>disk</code>, + <code>cdrom</code>, <code>floppy</code> and <code>lun</code>).</p> + + <h4><a name="elementsDisks">Hard drives, floppy disks, CDROMs</a></h4> + <p>Disk capabilities are exposed under <code>disk</code> element. For + instance:</p> + +<pre> +<domainCapabilities> + ... + <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>xen</value> + <value>usb</value> + <value>uml</value> + <value>sata</value> + <value>sd</value> + </enum> + </disk> + ... + </devices> +</domainCapabilities> +</pre> + + <dl> + <dt>diskDevice</dt> + <dd>What disk types are supported.</dd> + + <dt>bus</dt> + <dd>The bus within a domain that disk may occur on</dd> + </dl> + + <h4><a name="elementsHostDev">Host device assignment</a></h4> + <p>Some host devices can be passed through to a guest (e.g. USB, PCI and + SCSI). Well, only if the following is enabled:</p> + +<pre> +<domainCapabilities> + ... + <devices> + <hostdev supported='yes'> + <enum name='mode'> + <value>subsystem</value> + <value>capabilities</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'> + <value>storage</value> + <value>misc</value> + <value>net</value> + </enum> + <enum name='pciBackend'> + <value>default</value> + <value>kvm</value> + <value>vfio</value> + <value>xen</value> + </enum> + </hostdev> + </devices> +</domainCapabilities> +</pre> + + <dl> + <dt>mode</dt> + <dd>Describes what passthrough modes are supported</dd> + + <dt>startupPolicy</dt> + <dd>What startup policy modes are known to libvirt</dd> + + <dt>subsysType</dt> + <dd>Enumerates subsystems that are capable of passing through a device</dd> + + <dt>capsType</dt> + <dd>host device mode</dd> + + <dt>pciBackend</dt> + <dd>Which backend should be used to manage device passthrough</dd> + </dl> + </body> +</html> diff --git a/docs/schemas/Makefile.am b/docs/schemas/Makefile.am index d71c327..0e14dc6 100644 --- a/docs/schemas/Makefile.am +++ b/docs/schemas/Makefile.am @@ -19,6 +19,7 @@ schema_DATA = \ basictypes.rng \ capability.rng \ domain.rng \ + domaincaps.rng \ domaincommon.rng \ domainsnapshot.rng \ interface.rng \ diff --git a/docs/schemas/domaincaps.rng b/docs/schemas/domaincaps.rng new file mode 100644 index 0000000..627b699 --- /dev/null +++ b/docs/schemas/domaincaps.rng @@ -0,0 +1,90 @@ +<?xml version="1.0"?> +<!-- A Relax NG schema for the libvirt domain capabilities XML format --> +<grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"> + <include href='basictypes.rng'/> + <start> + <ref name='domainCapabilities'/> + </start> + + + <define name='domainCapabilities'> + <element name='domainCapabilities'> + <interleave> + <element name='path'> + <ref name="absFilePath"/> + </element> + <element name='domain'> + <text/> + </element> + <element name='machine'> + <text/> + </element> + <element name='arch'> + <text/> + </element> + <optional> + <ref name='vcpu'/> + </optional> + <optional> + <ref name='devices'/> + </optional> + </interleave> + </element> + </define> + + <define name='vcpu'> + <element name='vcpu'> + <attribute name='max'> + <ref name='unsignedInt'/> + </attribute> + <empty/> + </element> + </define> + + <define name='devices'> + <element name='devices'> + <interleave> + <ref name='disk'/> + <ref name='hostdev'/> + </interleave> + </element> + </define> + + <define name='disk'> + <element name='disk'> + <ref name='supported'/> + <ref name='enum'/> + </element> + </define> + + <define name='hostdev'> + <element name='hostdev'> + <ref name='supported'/> + <ref name='enum'/> + </element> + </define> + + <define name='supported'> + <attribute name='supported'> + <choice> + <value>yes</value> + <value>no</value> + </choice> + </attribute> + </define> + + <define name='enum'> + <zeroOrMore> + <element name='enum'> + <attribute name='name'> + <text/> + </attribute> + <zeroOrMore> + <element name='value'> + <text/> + </element> + </zeroOrMore> + </element> + </zeroOrMore> + </define> +</grammar> diff --git a/docs/sitemap.html.in b/docs/sitemap.html.in index 78e84e3..1e91869 100644 --- a/docs/sitemap.html.in +++ b/docs/sitemap.html.in @@ -175,6 +175,10 @@ <span>The driver capabilities XML format</span> </li> <li> + <a href="formatdomaincaps.html">Domain capabilities</a> + <span>The domain capabilities XML format</span> + </li> + <li> <a href="formatnode.html">Node Devices</a> <span>The host device XML format</span> </li> diff --git a/libvirt.spec.in b/libvirt.spec.in index 2ec7eed..f0bf737 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -2164,6 +2164,7 @@ exit 0 %{_datadir}/libvirt/schemas/basictypes.rng %{_datadir}/libvirt/schemas/capability.rng %{_datadir}/libvirt/schemas/domain.rng +%{_datadir}/libvirt/schemas/domaincaps.rng %{_datadir}/libvirt/schemas/domaincommon.rng %{_datadir}/libvirt/schemas/domainsnapshot.rng %{_datadir}/libvirt/schemas/interface.rng diff --git a/mingw-libvirt.spec.in b/mingw-libvirt.spec.in index 91c2dc2..a555f9c 100644 --- a/mingw-libvirt.spec.in +++ b/mingw-libvirt.spec.in @@ -205,6 +205,7 @@ rm -rf $RPM_BUILD_ROOT%{mingw64_libexecdir}/libvirt-guests.sh %{mingw32_datadir}/libvirt/schemas/basictypes.rng %{mingw32_datadir}/libvirt/schemas/capability.rng %{mingw32_datadir}/libvirt/schemas/domain.rng +%{mingw32_datadir}/libvirt/schemas/domaincaps.rng %{mingw32_datadir}/libvirt/schemas/domaincommon.rng %{mingw32_datadir}/libvirt/schemas/domainsnapshot.rng %{mingw32_datadir}/libvirt/schemas/interface.rng @@ -265,6 +266,7 @@ rm -rf $RPM_BUILD_ROOT%{mingw64_libexecdir}/libvirt-guests.sh %{mingw64_datadir}/libvirt/schemas/basictypes.rng %{mingw64_datadir}/libvirt/schemas/capability.rng %{mingw64_datadir}/libvirt/schemas/domain.rng +%{mingw64_datadir}/libvirt/schemas/domaincaps.rng %{mingw64_datadir}/libvirt/schemas/domaincommon.rng %{mingw64_datadir}/libvirt/schemas/domainsnapshot.rng %{mingw64_datadir}/libvirt/schemas/interface.rng diff --git a/po/POTFILES.in b/po/POTFILES.in index 31a8381..70fb6c2 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -16,6 +16,7 @@ src/conf/capabilities.c src/conf/cpu_conf.c src/conf/device_conf.c src/conf/domain_addr.c +src/conf/domain_capabilities.c src/conf/domain_conf.c src/conf/domain_event.c src/conf/interface_conf.c diff --git a/src/Makefile.am b/src/Makefile.am index 2b9ac61..e81af0c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -248,6 +248,7 @@ NETDEV_CONF_SOURCES = \ DOMAIN_CONF_SOURCES = \ conf/capabilities.c conf/capabilities.h \ conf/domain_addr.c conf/domain_addr.h \ + conf/domain_capabilities.c conf/domain_capabilities.h \ conf/domain_conf.c conf/domain_conf.h \ conf/domain_audit.c conf/domain_audit.h \ conf/domain_nwfilter.c conf/domain_nwfilter.h \ diff --git a/src/conf/domain_capabilities.c b/src/conf/domain_capabilities.c new file mode 100644 index 0000000..df190eb --- /dev/null +++ b/src/conf/domain_capabilities.c @@ -0,0 +1,254 @@ +/* + * domain_capabilities.c: domain capabilities XML processing + * + * Copyright (C) 2014 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Author: Michal Privoznik <mprivozn@redhat.com> + */ + +#include <config.h> + +#include "domain_capabilities.h" +#include "domain_conf.h" +#include "viralloc.h" +#include "virstring.h" + +#define VIR_FROM_THIS VIR_FROM_CAPABILITIES + +static virClassPtr virDomainCapsClass; + +static void virDomainCapsDispose(void *obj); + +static int virDomainCapsOnceInit(void) +{ + if (!(virDomainCapsClass = virClassNew(virClassForObjectLockable(), + "virDomainCapsClass", + sizeof(virDomainCaps), + virDomainCapsDispose))) + return -1; + return 0; +} + + +VIR_ONCE_GLOBAL_INIT(virDomainCaps) + + +static void +virDomainCapsDispose(void *obj) +{ + virDomainCapsPtr caps = obj; + + VIR_FREE(caps->path); + VIR_FREE(caps->machine); +} + + +virDomainCapsPtr +virDomainCapsNew(const char *path, + const char *machine, + virArch arch, + virDomainVirtType virttype) +{ + virDomainCapsPtr caps = NULL; + + if (virDomainCapsInitialize() < 0) + return NULL; + + if (!(caps = virObjectLockableNew(virDomainCapsClass))) + return NULL; + + if (VIR_STRDUP(caps->path, path) < 0 || + VIR_STRDUP(caps->machine, machine) < 0) + goto error; + caps->arch = arch; + caps->virttype = virttype; + + return caps; + error: + virObjectUnref(caps); + return NULL; +} + + +int +virDomainCapsEnumSet(virDomainCapsEnumPtr capsEnum, + const char *capsEnumName, + size_t nvalues, + unsigned int *values) +{ + int ret = -1; + size_t i; + + for (i = 0; i < nvalues; i++) { + unsigned int val = 1 << values[i]; + + if (!val) { + /* Integer overflow */ + virReportError(VIR_ERR_INTERNAL_ERROR, + _("integer overflow on %s. Please contact the " + "libvirt development team at libvir-list@redhat.com"), + capsEnumName); + goto cleanup; + } + + capsEnum->values |= val; + } + + ret = 0; + cleanup: + return ret; +} + + +void +virDomainCapsEnumClear(virDomainCapsEnumPtr capsEnum) +{ + capsEnum->values = 0; +} + + +static int +virDomainCapsEnumFormat(virBufferPtr buf, + virDomainCapsEnumPtr capsEnum, + const char *capsEnumName, + virDomainCapsValToStr valToStr) +{ + int ret = -1; + size_t i; + + virBufferAsprintf(buf, "<enum name='%s'", capsEnumName); + if (!capsEnum->values) { + virBufferAddLit(buf, "/>\n"); + ret = 0; + goto cleanup; + } + virBufferAddLit(buf, ">\n"); + virBufferAdjustIndent(buf, 2); + + for (i = 0; i < sizeof(capsEnum->values) * CHAR_BIT; i++) { + const char *val; + + if (!(capsEnum->values & (1 << i))) + continue; + + if ((val = (valToStr)(i))) + virBufferAsprintf(buf, "<value>%s</value>\n", val); + } + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</enum>\n"); + + ret = 0; + cleanup: + return ret; +} + +#define FORMAT_PROLOGUE(item) \ + do { \ + virBufferAsprintf(buf, "<" #item " supported='%s'%s\n", \ + item->device.supported ? "yes" : "no", \ + item->device.supported ? ">" : "/>"); \ + if (!item->device.supported) \ + return; \ + virBufferAdjustIndent(buf, 2); \ + } while (0) + +#define FORMAT_EPILOGUE(item) \ + do { \ + virBufferAdjustIndent(buf, -2); \ + virBufferAddLit(buf, "</" #item ">\n"); \ + } while (0) + +#define ENUM_PROCESS(master, capsEnum, valToStr) \ + do { \ + virDomainCapsEnumFormat(buf, &master->capsEnum, \ + #capsEnum, valToStr); \ + } while (0) + +static void +virDomainCapsDeviceDiskFormat(virBufferPtr buf, + virDomainCapsDeviceDiskPtr const disk) +{ + FORMAT_PROLOGUE(disk); + + ENUM_PROCESS(disk, diskDevice, virDomainDiskDeviceTypeToString); + ENUM_PROCESS(disk, bus, virDomainDiskBusTypeToString); + + FORMAT_EPILOGUE(disk); +} + + +static void +virDomainCapsDeviceHostdevFormat(virBufferPtr buf, + virDomainCapsDeviceHostdevPtr const hostdev) +{ + FORMAT_PROLOGUE(hostdev); + + ENUM_PROCESS(hostdev, mode, virDomainHostdevModeTypeToString); + ENUM_PROCESS(hostdev, startupPolicy, virDomainStartupPolicyTypeToString); + ENUM_PROCESS(hostdev, subsysType, virDomainHostdevSubsysTypeToString); + ENUM_PROCESS(hostdev, capsType, virDomainHostdevCapsTypeToString); + ENUM_PROCESS(hostdev, pciBackend, virDomainHostdevSubsysPCIBackendTypeToString); + + FORMAT_EPILOGUE(hostdev); +} + + +static int +virDomainCapsFormatInternal(virBufferPtr buf, + virDomainCapsPtr const caps) +{ + const char *virttype_str = virDomainVirtTypeToString(caps->virttype); + const char *arch_str = virArchToString(caps->arch); + + virBufferAddLit(buf, "<domainCapabilities>\n"); + virBufferAdjustIndent(buf, 2); + + virBufferAsprintf(buf, "<path>%s</path>\n", caps->path); + virBufferAsprintf(buf, "<domain>%s</domain>\n", virttype_str); + virBufferAsprintf(buf, "<machine>%s</machine>\n", caps->machine); + virBufferAsprintf(buf, "<arch>%s</arch>\n", arch_str); + + if (caps->maxvcpus) + virBufferAsprintf(buf, "<vcpu max='%d'/>\n", caps->maxvcpus); + + virBufferAddLit(buf, "<devices>\n"); + virBufferAdjustIndent(buf, 2); + + virDomainCapsDeviceDiskFormat(buf, &caps->disk); + virDomainCapsDeviceHostdevFormat(buf, &caps->hostdev); + + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</devices>\n"); + + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</domainCapabilities>\n"); + return 0; +} + + +char * +virDomainCapsFormat(virDomainCapsPtr const caps) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + + if (virDomainCapsFormatInternal(&buf, caps) < 0) { + virBufferFreeAndReset(&buf); + return NULL; + } + + return virBufferContentAndReset(&buf); +} diff --git a/src/conf/domain_capabilities.h b/src/conf/domain_capabilities.h new file mode 100644 index 0000000..731e66f --- /dev/null +++ b/src/conf/domain_capabilities.h @@ -0,0 +1,103 @@ +/* + * domain_capabilities.h: domain capabilities XML processing + * + * Copyright (C) 2014 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Author: Michal Privoznik <mprivozn@redhat.com> + */ + +#ifndef __DOMAIN_CAPABILITIES_H__ +# define __DOMAIN_CAPABILITIES_H__ + +# include "internal.h" +# include "domain_conf.h" + +typedef const char * (*virDomainCapsValToStr)(int value); + +typedef struct _virDomainCaps virDomainCaps; +typedef virDomainCaps *virDomainCapsPtr; + +typedef struct _virDomainCapsEnum virDomainCapsEnum; +typedef virDomainCapsEnum *virDomainCapsEnumPtr; +struct _virDomainCapsEnum { + unsigned int values; /* Bitmask of values supported in the corresponding enum */ +}; + +typedef struct _virDomainCapsDevice virDomainCapsDevice; +typedef virDomainCapsDevice *virDomainCapsDevicePtr; +struct _virDomainCapsDevice { + bool supported; /* true if <devtype> is supported by hypervisor */ +}; + +typedef struct _virDomainCapsDeviceDisk virDomainCapsDeviceDisk; +typedef virDomainCapsDeviceDisk *virDomainCapsDeviceDiskPtr; +struct _virDomainCapsDeviceDisk { + virDomainCapsDevice device; + virDomainCapsEnum diskDevice; /* Info about virDomainDiskDevice enum values */ + virDomainCapsEnum bus; /* Info about virDomainDiskBus enum values */ + /* add new fields here */ +}; + +typedef struct _virDomainCapsDeviceHostdev virDomainCapsDeviceHostdev; +typedef virDomainCapsDeviceHostdev *virDomainCapsDeviceHostdevPtr; +struct _virDomainCapsDeviceHostdev { + virDomainCapsDevice device; + virDomainCapsEnum mode; /* Info about virDomainHostdevMode */ + virDomainCapsEnum startupPolicy; /* Info about virDomainStartupPolicy */ + virDomainCapsEnum subsysType; /* Info about virDomainHostdevSubsysType */ + virDomainCapsEnum capsType; /* Info about virDomainHostdevCapsType */ + virDomainCapsEnum pciBackend; /* Info about virDomainHostdevSubsysPCIBackendType */ + /* add new fields here */ +}; + +struct _virDomainCaps { + virObjectLockable parent; + + char *path; /* path to emulator binary */ + virDomainVirtType virttype; /* virtualization type */ + char *machine; /* machine type */ + virArch arch; /* domain architecture */ + + /* Some machine specific info */ + int maxvcpus; + + virDomainCapsDeviceDisk disk; + virDomainCapsDeviceHostdev hostdev; + /* add new domain devices here */ +}; + +virDomainCapsPtr virDomainCapsNew(const char *path, + const char *machine, + virArch arch, + virDomainVirtType virttype); + +# define VIR_DOMAIN_CAPS_ENUM_SET(capsEnum, ...) \ + do { \ + unsigned int __values[] = {__VA_ARGS__}; \ + size_t __nvalues = ARRAY_CARDINALITY(__values); \ + virDomainCapsEnumSet(&(capsEnum), #capsEnum, \ + __nvalues, __values); \ + } while (0) + +int virDomainCapsEnumSet(virDomainCapsEnumPtr capsEnum, + const char *capsEnumName, + size_t nvalues, + unsigned int *values); +void virDomainCapsEnumClear(virDomainCapsEnumPtr capsEnum); + +char * virDomainCapsFormat(virDomainCapsPtr const caps); +#endif /* __DOMAIN_CAPABILITIES_H__ */ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 1e1dd84..40585bd 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -130,6 +130,13 @@ virDomainAuditStop; virDomainAuditVcpu; +# conf/domain_capabilities.h +virDomainCapsEnumClear; +virDomainCapsEnumSet; +virDomainCapsFormat; +virDomainCapsNew; + + # conf/domain_conf.h virBlkioDeviceArrayClear; virDiskNameToBusDeviceIndex; diff --git a/tests/Makefile.am b/tests/Makefile.am index 025b847..97af0d9 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -74,6 +74,8 @@ EXTRA_DIST = \ commanddata \ confdata \ cputestdata \ + domaincapsschemadata \ + domaincapsschematest \ domainconfdata \ domainschemadata \ domainschematest \ @@ -167,6 +169,7 @@ test_programs = virshtest sockettest \ virnetdevbandwidthtest \ virkmodtest \ vircapstest \ + domaincapstest \ domainconftest \ virhostdevtest \ vircaps2xmltest \ @@ -318,6 +321,7 @@ test_scripts = \ networkschematest \ storagepoolschematest \ storagevolschematest \ + domaincapsschematest \ domainschematest \ nodedevschematest \ nwfilterschematest \ @@ -827,6 +831,10 @@ vircaps2xmltest_SOURCES = \ vircaps2xmltest.c testutils.h testutils.c vircaps2xmltest_LDADD = $(LDADDS) +domaincapstest_SOURCES = \ + domaincapstest.c testutils.h testutils.c +domaincapstest_LDADD = $(LDADDS) + if WITH_LIBVIRTD libvirtdconftest_SOURCES = \ libvirtdconftest.c testutils.h testutils.c \ diff --git a/tests/domaincapsschemadata/domaincaps-basic.xml b/tests/domaincapsschemadata/domaincaps-basic.xml new file mode 100644 index 0000000..9963519 --- /dev/null +++ b/tests/domaincapsschemadata/domaincaps-basic.xml @@ -0,0 +1,10 @@ +<domainCapabilities> + <path>/bin/emulatorbin</path> + <domain>uml</domain> + <machine>my-machine-type</machine> + <arch>x86_64</arch> + <devices> + <disk supported='no'/> + <hostdev supported='no'/> + </devices> +</domainCapabilities> diff --git a/tests/domaincapsschemadata/domaincaps-full.xml b/tests/domaincapsschemadata/domaincaps-full.xml new file mode 100644 index 0000000..58dd4cb --- /dev/null +++ b/tests/domaincapsschemadata/domaincaps-full.xml @@ -0,0 +1,56 @@ +<domainCapabilities> + <path>/bin/emulatorbin</path> + <domain>kvm</domain> + <machine>my-machine-type</machine> + <arch>x86_64</arch> + <vcpu max='255'/> + <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>xen</value> + <value>usb</value> + <value>uml</value> + <value>sata</value> + <value>sd</value> + </enum> + </disk> + <hostdev supported='yes'> + <enum name='mode'> + <value>subsystem</value> + <value>capabilities</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'> + <value>storage</value> + <value>misc</value> + <value>net</value> + </enum> + <enum name='pciBackend'> + <value>default</value> + <value>kvm</value> + <value>vfio</value> + <value>xen</value> + </enum> + </hostdev> + </devices> +</domainCapabilities> diff --git a/tests/domaincapsschematest b/tests/domaincapsschematest new file mode 100755 index 0000000..9baf44a --- /dev/null +++ b/tests/domaincapsschematest @@ -0,0 +1,11 @@ +#!/bin/sh + +: ${srcdir=.} +. $srcdir/test-lib.sh +. $abs_srcdir/schematestutils.sh + +DIRS="" +DIRS="$DIRS domaincapsschemadata" +SCHEMA="domaincaps.rng" + +check_schema "$DIRS" "$SCHEMA" diff --git a/tests/domaincapstest.c b/tests/domaincapstest.c new file mode 100644 index 0000000..6cdd086 --- /dev/null +++ b/tests/domaincapstest.c @@ -0,0 +1,149 @@ +/* + * Copyright (C) Red Hat, Inc. 2014 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Authors: + * Michal Privoznik <mprivozn@redhat.com> + */ + +#include <config.h> +#include <stdlib.h> + +#include "testutils.h" +#include "domain_capabilities.h" + + +#define VIR_FROM_THIS VIR_FROM_NONE + +typedef void (*virDomainCapsFill)(virDomainCapsPtr domCaps, + void *opaque); + +#define SET_ALL_BITS(x) \ + memset(&(x.values), 0xff, sizeof(x.values)) + +static void +fillAll(virDomainCapsPtr domCaps, + void *opaque ATTRIBUTE_UNUSED) +{ + virDomainCapsDeviceDiskPtr disk = &domCaps->disk; + virDomainCapsDeviceHostdevPtr hostdev = &domCaps->hostdev; + domCaps->maxvcpus = 255; + + disk->device.supported = true; + SET_ALL_BITS(disk->diskDevice); + SET_ALL_BITS(disk->bus); + + hostdev->device.supported = true; + SET_ALL_BITS(hostdev->mode); + SET_ALL_BITS(hostdev->startupPolicy); + SET_ALL_BITS(hostdev->subsysType); + SET_ALL_BITS(hostdev->capsType); + SET_ALL_BITS(hostdev->pciBackend); +} + +static virDomainCapsPtr +buildVirDomainCaps(const char *emulatorbin, + const char *machine, + virArch arch, + virDomainVirtType type, + virDomainCapsFill fillFunc, + void *opaque) +{ + virDomainCapsPtr domCaps; + + if (!(domCaps = virDomainCapsNew(emulatorbin, machine, arch, type))) + goto cleanup; + + if (fillFunc) + fillFunc(domCaps, opaque); + + cleanup: + return domCaps; +} + +struct test_virDomainCapsFormatData { + const char *filename; + const char *emulatorbin; + const char *machine; + virArch arch; + virDomainVirtType type; + virDomainCapsFill fillFunc; + void *opaque; +}; + +static int +test_virDomainCapsFormat(const void *opaque) +{ + struct test_virDomainCapsFormatData *data = + (struct test_virDomainCapsFormatData *) opaque; + virDomainCapsPtr domCaps = NULL; + char *path = NULL; + char *domCapsXML = NULL; + char *domCapsFromFile = NULL; + int ret = -1; + + if (virAsprintf(&path, "%s/domaincapsschemadata/domaincaps-%s.xml", + abs_srcdir, data->filename) < 0) + goto cleanup; + + if (virFileReadAll(path, 8192, &domCapsFromFile) < 0) + goto cleanup; + + if (!(domCaps = buildVirDomainCaps(data->emulatorbin, data->machine, + data->arch, data->type, + data->fillFunc, data->opaque))) + goto cleanup; + + if (!(domCapsXML = virDomainCapsFormat(domCaps))) + goto cleanup; + + if (STRNEQ(domCapsFromFile, domCapsXML)) { + virtTestDifference(stderr, domCapsFromFile, domCapsXML); + goto cleanup; + } + + ret = 0; + cleanup: + VIR_FREE(domCapsFromFile); + VIR_FREE(domCapsXML); + VIR_FREE(path); + virObjectUnref(domCaps); + return ret; +} + +static int +mymain(void) +{ + int ret = 0; + +#define DO_TEST(Filename, Emulatorbin, Machine, Arch, Type, ...) \ + do { \ + struct test_virDomainCapsFormatData data = {.filename = Filename, \ + .emulatorbin = Emulatorbin, .machine = Machine, .arch = Arch, \ + .type = Type, __VA_ARGS__}; \ + if (virtTestRun(Filename, test_virDomainCapsFormat, &data) < 0) \ + ret = -1; \ + } while (0) + + DO_TEST("basic", "/bin/emulatorbin", "my-machine-type", + VIR_ARCH_X86_64, VIR_DOMAIN_VIRT_UML); + DO_TEST("full", "/bin/emulatorbin", "my-machine-type", + VIR_ARCH_X86_64, VIR_DOMAIN_VIRT_KVM, .fillFunc = fillAll); + + return ret; +} + +VIRT_TEST_MAIN(mymain) -- 1.8.5.5

On Mon, Jun 30, 2014 at 05:31:44PM +0200, Michal Privoznik wrote:
This new module holds and formats capabilities for emulator. If you are about to create a new domain, you may want to know what is the host or hypervisor capable of. To make sure we don't regress on the XML, the formatting is not something left for each driver to implement, rather there's general format function.
The domain capabilities is a lockable object (even though the locking is not necessary yet) which uses reference counter.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- docs/formatdomaincaps.html.in | 200 +++++++++++++++++++ docs/schemas/Makefile.am | 1 + docs/schemas/domaincaps.rng | 90 +++++++++ docs/sitemap.html.in | 4 + libvirt.spec.in | 1 + mingw-libvirt.spec.in | 2 + po/POTFILES.in | 1 + src/Makefile.am | 1 + src/conf/domain_capabilities.c | 254 ++++++++++++++++++++++++ src/conf/domain_capabilities.h | 103 ++++++++++ src/libvirt_private.syms | 7 + tests/Makefile.am | 8 + tests/domaincapsschemadata/domaincaps-basic.xml | 10 + tests/domaincapsschemadata/domaincaps-full.xml | 56 ++++++ tests/domaincapsschematest | 11 + tests/domaincapstest.c | 149 ++++++++++++++ 16 files changed, 898 insertions(+) create mode 100644 docs/formatdomaincaps.html.in create mode 100644 docs/schemas/domaincaps.rng create mode 100644 src/conf/domain_capabilities.c create mode 100644 src/conf/domain_capabilities.h create mode 100644 tests/domaincapsschemadata/domaincaps-basic.xml create mode 100644 tests/domaincapsschemadata/domaincaps-full.xml create mode 100755 tests/domaincapsschematest create mode 100644 tests/domaincapstest.c
ACK Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 06/30/2014 11:31 AM, Michal Privoznik wrote:
This new module holds and formats capabilities for emulator. If you are about to create a new domain, you may want to know what is the host or hypervisor capable of. To make sure we don't regress on the XML, the formatting is not something left for each driver to implement, rather there's general format function.
The domain capabilities is a lockable object (even though the locking is not necessary yet) which uses reference counter.
I started looking at these yesterday, but ended up having more questions than I originally thought I would, so perhaps better to get some answered. Of course it's already too wordy (sorry)... I was trying to envision use cases - that is how is this expected to be used and what "knowledge" is assumed of the caller/user vs. being designed to a more naive user trying to glean information about what's available. You have a very specific use case described - can I determine if vfio is supported, but that requires someone knowing quite a bit of information that isn't easily accessible unless you read sources or have a bit of history built up. For the domcapabilities command that eventually gets added - how does one know what to provide for the 4 options without knowing a bit about the environment. It seems the assumption is the user knows to pass certain elements. The 'virttype' is pretty easy - that comes from the connection - so I wonder why it's a parameter to be provided. Does one really have to have a connection to get the data? The 'emulatorbin' is less obvious. If it's not passed, there is a way to get the default value given that you have a virttype, an os type, and an os arch using virCapabilitiesDefaultGuestEmulator(). What if someone provides "/usr/bin/qemu-kvm" or "/usr/libexec/qemu-kvm" or is there an expectation of /usr/bin/qemu-system-x86_64? The 'arch' requires a bit more knowledge, but is certainly "obtainable" as a default by the current host arch, right? There's also virCapabilitiesDefaultGuestArch(). However, if someone was looking to find out what was running on the remote connection (not the local machine), then that assumption would be incorrect. Seems we should be able to figure out what arch is associated with the connection. I think 'machine' is perhaps the most odd to provide; however, like arch and emulatorbin, there is virCapabilitiesDefaultGuestMachine() to help you out. For this if one passed "pc" does that work - or do they have to pass something like "pc-i440fx-1.6" with the next question being how would they know to generate that? Reading the domain_conf code - only 'virttype' and 'os_type' really seem to be required - everything else can be figured out "by default" given the two. Having (a) virsh command(s) to display possible options may be a nice addition, especially for the naive user... Should be very easy to add something that could print out the virArch options. Leaving only the need to know what the os_type is before behing able to at least fetch defaults and generate XML output. Perhaps someone just using "virsh domcapabilities" would print out tables of all arch's.. Similarly if arch was provided, then print out all emulatorbin's (if that's possible) for the arch or just the default emulatorbin... Given an arch and emulatorbin, then print out the machines available.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- docs/formatdomaincaps.html.in | 200 +++++++++++++++++++ docs/schemas/Makefile.am | 1 + docs/schemas/domaincaps.rng | 90 +++++++++ docs/sitemap.html.in | 4 + libvirt.spec.in | 1 + mingw-libvirt.spec.in | 2 + po/POTFILES.in | 1 + src/Makefile.am | 1 + src/conf/domain_capabilities.c | 254 ++++++++++++++++++++++++ src/conf/domain_capabilities.h | 103 ++++++++++ src/libvirt_private.syms | 7 + tests/Makefile.am | 8 + tests/domaincapsschemadata/domaincaps-basic.xml | 10 + tests/domaincapsschemadata/domaincaps-full.xml | 56 ++++++ tests/domaincapsschematest | 11 + tests/domaincapstest.c | 149 ++++++++++++++ 16 files changed, 898 insertions(+) create mode 100644 docs/formatdomaincaps.html.in create mode 100644 docs/schemas/domaincaps.rng create mode 100644 src/conf/domain_capabilities.c create mode 100644 src/conf/domain_capabilities.h create mode 100644 tests/domaincapsschemadata/domaincaps-basic.xml create mode 100644 tests/domaincapsschemadata/domaincaps-full.xml create mode 100755 tests/domaincapsschematest create mode 100644 tests/domaincapstest.c
diff --git a/docs/formatdomaincaps.html.in b/docs/formatdomaincaps.html.in new file mode 100644 index 0000000..cfd61d9 --- /dev/null +++ b/docs/formatdomaincaps.html.in
Since there's a "Capabilities" page describing the Driver (Host/Guest) Capabilities and now this one for Domain - do you see a future need for storage, network, etc. type capabilities? If so, other than the "volume of data" then why not extend capabilities so that it could list everything available for everything we know about? e.g.: <capabilities> <host... /> <guest... /> <domain... /> <storage... /> <network... /> <interface... /> </capabilities> Architecturally, does it make sense to merge them or keep then separate? There seems to be a relationship between them. Furthermore, if the goal is to just provide information, then using one pile of xml output to store everything may be useful to someone. For the more seasoned individual using a specific/directed API that is planned will provide the directed answer. I think the 'virsh capabilies' output certainly shows it's possible to grab/use the various arch, machine, emulator, and domain type values to generate lengthy output listing every possible option. [1]If this page is kept, then perhaps modify the sitemap heading to be "Host/Guest" or "Driver" Capabilities... I also think perhaps that this .in file could wait until at least patch 2 where virConnectGetDomainCapabilities() is exposed... So a pointer to it can be added. BTW: Look at api.html.in - there are examples on how to directly link the API to the API description, eg. "<code class="docref">virConnectGetDomainCapabilities</code>"
@@ -0,0 +1,200 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> + <body> + <h1>Domain capabilities XML format</h1> + + <ul id="toc"></ul> + + <h2><a name="Motivation">Motivation</a></h2>
s/Motivation/Overview/
+ + <p>Sometimes, when a new domain is to be created it may come handy to know + the capabilities of the hypervisor so the correct combination of devices and + drivers is used. For example, when management application is considering the + mode for a host device's passthrough there are several options depending not + only on host, but on hypervisor in question too. If the hypervisor is qemu + then it needs to be more recent to support VFIO, while legacy KVM is + achievable just fine with older one.</p>
"older one" ??? Contextually what is the older one? Naive or newer user can be confused...
+ + <p>The main difference between <a + href="formatcaps.html">virConnectGetCapabilities</a> and the emulator + capabilities API is, the former one aims more on the host capabilities (e.g. + NUMA topology, security models in effect, etc.) while the latter one + specializes on the hypervisor capabilities.</p>
[assuming this page sticks... Consider...] While the <a href="formatcaps.html">Driver Capabilities</a> provides the host capabilities (e.g NUMA topology, security models in effect, etc.), the Domain Capabilities provides the hypervisor specific capabilities for Management Applications to query and make decisions regarding what to utilize. The Domain Capabilities [can|will] provide information such as the correct combination of devices and drivers that are supported. Knowing which host and hypervisor specific options are available or supported would allow the management application to choose an appropriate mode for a pass-through host device as well as which adapter to utilize. [does this make sense] For example, choosing a specific host device for pass-through device using "vfio" requires a specific QEMU version to be installed; whereas, using ["????"] is the older legacy model employed by KVM.
+ + <h2><a name="elements">Element and attribute overview</a></h2> +
[Also like the formatcaps.html.in page - a <pre> </pre> with just the API listed would be nice - although I wouldn't put the args there - just the pointer, such as:] <p> A new query interface was added to the virConnect API's to retrieve the XML listing of the set of domain capabilities: <pre> <code class="docref">virConnectGetDomainCapabilities</code> </pre> <span class="since">Since 1.2.7</span>.</p> [I also think formatcaps.html.in should use the same <code class="docref">...</code> syntax, but that's a different issue] [Perhaps some virsh command options that can be provided to display the information. Similarly adding a <pre> </pre> on the capabilities page to show how to get the information using virsh capabilities, but again that's a different issue. ]
+ <p>The root element that emulator capability XML document starts with has + name <code>domainCapabilities</code>. It contains at least four direct + child elements:</p> + +<pre> +<domainCapabilities> + <path>/usr/bin/qemu-system-x86_64</path> + <domain>kvm</domain> + <machine>pc-i440fx-2.1</machine> + <arch>x86_64</arch> + ... +</domainCapabilities> +</pre> + <dl> + <dt>path</dt> + <dd>The full path to the emulator binary.</dd> + + <dt>domain</dt> + <dd>Describes the <a href="formatdomain.html#elements">virtualization + type</a> (or so called domain type).</dd>
Personally <domain> seems redundant. Perhaps follow code and use <virttype>...
+ + <dt>machine</dt> + <dd>The domain's <a href="formatdomain.html#elementsOSBIOS">machine + type</a>.</dd>
Somewhat of a nit, but the link provides only vague information about <machine> and points off to the formatcaps.html.in page which provides just "pc" and "isapc" as "<machine>" examples. Going back to a comment I made earlier about how does one know what to provide...
+ + <dt>arch</dt> + <dd>The domain's <a href="formatdomain.html#elementsOSBIOS"> + architecture</a>.</dd> +
Similar, but at least there is an "<arch name="..."/> found. The point being <machine> and <arch> are discussed in the <type> explanation - it's not well described.
+ </dl> + + <h3><a name="elementsCPUAllocation">CPU Allocation</a></h3> +
There's no textual context/introduction here.
+<pre> +<domainCapabilities> + ... + <vcpu max='255'/> + ... +</domainCapabilities> +</pre> + + <dl> + <dt>vcpu</dt> + <dd>The maximum number of supported virtual CPUs</dd> + </dl> + + <h3><a name="elementsDevices">Devices</a></h3> + + <p> + The final set of XML elements are all used to describe supported devices + and their capabilities. All devices occur as children of the main + <code>devices</code> element. + </p> +
s/are all used to describe/describe the/
+<pre> +<domainCapabilities> + ... + <devices> + <disk supported='yes'> + <enum name='diskDevice'> + <value>disk</value> + <value>cdrom</value> + <value>floppy</value> + <value>lun</value> + </enum> + ... + </disk> + <hostdev supported='no'/> + </devices> +</domainCapabilities> +</pre> + + <p>All reported capabilities share the way that they are expressed. In the + example above we can see what disk devices are supported (<code>disk</code>, + <code>cdrom</code>, <code>floppy</code> and <code>lun</code>).</p>
[Consider] Reported capabilities are expressed as an enumerated list of available options for each of the element or attribute. For example, the "<disk>" element has an attribute "device=" which can support the values <code>disk</code>, <code>cdrom</code>, <code>floppy</code>, or <code>lun</code>.
+ + <h4><a name="elementsDisks">Hard drives, floppy disks, CDROMs</a></h4> + <p>Disk capabilities are exposed under <code>disk</code> element. For + instance:</p> + +<pre> +<domainCapabilities> + ... + <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>xen</value> + <value>usb</value> + <value>uml</value> + <value>sata</value> + <value>sd</value> + </enum> + </disk> + ... + </devices> +</domainCapabilities> +</pre> + + <dl> + <dt>diskDevice</dt> + <dd>What disk types are supported.</dd>
[consider] Options for the "device" attribute of the "<disk>" element.
+ + <dt>bus</dt> + <dd>The bus within a domain that disk may occur on</dd>
[consider] Options for the "bus" attribute of the "<target>" element for a "<disk>".
+ </dl> + + <h4><a name="elementsHostDev">Host device assignment</a></h4> + <p>Some host devices can be passed through to a guest (e.g. USB, PCI and + SCSI). Well, only if the following is enabled:</p> +
s/. Well, only if the following is enabled/ as long as the <code>supported</code> attribute is set to "yes"./
+<pre> +<domainCapabilities> + ... + <devices> + <hostdev supported='yes'> + <enum name='mode'> + <value>subsystem</value> + <value>capabilities</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'> + <value>storage</value> + <value>misc</value> + <value>net</value> + </enum> + <enum name='pciBackend'> + <value>default</value> + <value>kvm</value> + <value>vfio</value> + <value>xen</value> + </enum> + </hostdev> + </devices> +</domainCapabilities> +</pre> + + <dl> + <dt>mode</dt> + <dd>Describes what passthrough modes are supported</dd> +
[consider] Options for the "mode" attribute of the "<hostdev>" element. [similarly for the rest]
+ <dt>startupPolicy</dt> + <dd>What startup policy modes are known to libvirt</dd> + + <dt>subsysType</dt> + <dd>Enumerates subsystems that are capable of passing through a device</dd> + + <dt>capsType</dt> + <dd>host device mode</dd> + + <dt>pciBackend</dt> + <dd>Which backend should be used to manage device passthrough</dd> + </dl> + </body> +</html> diff --git a/docs/schemas/Makefile.am b/docs/schemas/Makefile.am index d71c327..0e14dc6 100644 --- a/docs/schemas/Makefile.am +++ b/docs/schemas/Makefile.am @@ -19,6 +19,7 @@ schema_DATA = \ basictypes.rng \ capability.rng \ domain.rng \ + domaincaps.rng \ domaincommon.rng \ domainsnapshot.rng \ interface.rng \ diff --git a/docs/schemas/domaincaps.rng b/docs/schemas/domaincaps.rng new file mode 100644 index 0000000..627b699 --- /dev/null +++ b/docs/schemas/domaincaps.rng @@ -0,0 +1,90 @@ +<?xml version="1.0"?> +<!-- A Relax NG schema for the libvirt domain capabilities XML format --> +<grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"> + <include href='basictypes.rng'/> + <start> + <ref name='domainCapabilities'/> + </start> + + + <define name='domainCapabilities'> + <element name='domainCapabilities'> + <interleave> + <element name='path'> + <ref name="absFilePath"/> + </element> + <element name='domain'>
Like noted before should change - perhaps model code "virttype"
+ <text/> + </element> + <element name='machine'> + <text/> + </element> + <element name='arch'> + <text/> + </element> + <optional> + <ref name='vcpu'/> + </optional> + <optional> + <ref name='devices'/> + </optional> + </interleave> + </element> + </define> + + <define name='vcpu'> + <element name='vcpu'> + <attribute name='max'> + <ref name='unsignedInt'/> + </attribute> + <empty/> + </element> + </define> + + <define name='devices'> + <element name='devices'> + <interleave> + <ref name='disk'/> + <ref name='hostdev'/> + </interleave> + </element> + </define> + + <define name='disk'> + <element name='disk'> + <ref name='supported'/> + <ref name='enum'/> + </element> + </define> + + <define name='hostdev'> + <element name='hostdev'> + <ref name='supported'/> + <ref name='enum'/> + </element> + </define> + + <define name='supported'> + <attribute name='supported'> + <choice> + <value>yes</value> + <value>no</value> + </choice> + </attribute> + </define> + + <define name='enum'> + <zeroOrMore> + <element name='enum'> + <attribute name='name'> + <text/> + </attribute> + <zeroOrMore> + <element name='value'> + <text/> + </element> + </zeroOrMore> + </element> + </zeroOrMore> + </define> +</grammar> diff --git a/docs/sitemap.html.in b/docs/sitemap.html.in index 78e84e3..1e91869 100644 --- a/docs/sitemap.html.in +++ b/docs/sitemap.html.in
[1] Possible change to formatcaps.html to be "Driver capabilities" instead of just "Capabilities"
@@ -175,6 +175,10 @@ <span>The driver capabilities XML format</span>
[1] Possible text change to use "Host/Guest" Capabilities?
</li> <li> + <a href="formatdomaincaps.html">Domain capabilities</a> + <span>The domain capabilities XML format</span> + </li> + <li> <a href="formatnode.html">Node Devices</a> <span>The host device XML format</span> </li> diff --git a/libvirt.spec.in b/libvirt.spec.in index 2ec7eed..f0bf737 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -2164,6 +2164,7 @@ exit 0 %{_datadir}/libvirt/schemas/basictypes.rng %{_datadir}/libvirt/schemas/capability.rng %{_datadir}/libvirt/schemas/domain.rng +%{_datadir}/libvirt/schemas/domaincaps.rng %{_datadir}/libvirt/schemas/domaincommon.rng %{_datadir}/libvirt/schemas/domainsnapshot.rng %{_datadir}/libvirt/schemas/interface.rng
It seems something is always missed in the spec file - is this all that's necessary? I think the po and html files are included en masse, but I guess better safe than sorry... The question being asked - did you build rpm or dist (mumblyfratz)?
diff --git a/mingw-libvirt.spec.in b/mingw-libvirt.spec.in index 91c2dc2..a555f9c 100644 --- a/mingw-libvirt.spec.in +++ b/mingw-libvirt.spec.in @@ -205,6 +205,7 @@ rm -rf $RPM_BUILD_ROOT%{mingw64_libexecdir}/libvirt-guests.sh %{mingw32_datadir}/libvirt/schemas/basictypes.rng %{mingw32_datadir}/libvirt/schemas/capability.rng %{mingw32_datadir}/libvirt/schemas/domain.rng +%{mingw32_datadir}/libvirt/schemas/domaincaps.rng %{mingw32_datadir}/libvirt/schemas/domaincommon.rng %{mingw32_datadir}/libvirt/schemas/domainsnapshot.rng %{mingw32_datadir}/libvirt/schemas/interface.rng @@ -265,6 +266,7 @@ rm -rf $RPM_BUILD_ROOT%{mingw64_libexecdir}/libvirt-guests.sh %{mingw64_datadir}/libvirt/schemas/basictypes.rng %{mingw64_datadir}/libvirt/schemas/capability.rng %{mingw64_datadir}/libvirt/schemas/domain.rng +%{mingw64_datadir}/libvirt/schemas/domaincaps.rng %{mingw64_datadir}/libvirt/schemas/domaincommon.rng %{mingw64_datadir}/libvirt/schemas/domainsnapshot.rng %{mingw64_datadir}/libvirt/schemas/interface.rng
^^^and of course the other most forgotten area...^^^
diff --git a/po/POTFILES.in b/po/POTFILES.in index 31a8381..70fb6c2 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -16,6 +16,7 @@ src/conf/capabilities.c src/conf/cpu_conf.c src/conf/device_conf.c src/conf/domain_addr.c +src/conf/domain_capabilities.c src/conf/domain_conf.c src/conf/domain_event.c src/conf/interface_conf.c diff --git a/src/Makefile.am b/src/Makefile.am index 2b9ac61..e81af0c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -248,6 +248,7 @@ NETDEV_CONF_SOURCES = \ DOMAIN_CONF_SOURCES = \ conf/capabilities.c conf/capabilities.h \ conf/domain_addr.c conf/domain_addr.h \ + conf/domain_capabilities.c conf/domain_capabilities.h \ conf/domain_conf.c conf/domain_conf.h \ conf/domain_audit.c conf/domain_audit.h \ conf/domain_nwfilter.c conf/domain_nwfilter.h \ diff --git a/src/conf/domain_capabilities.c b/src/conf/domain_capabilities.c new file mode 100644 index 0000000..df190eb --- /dev/null +++ b/src/conf/domain_capabilities.c @@ -0,0 +1,254 @@ +/* + * domain_capabilities.c: domain capabilities XML processing + * + * Copyright (C) 2014 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Author: Michal Privoznik <mprivozn@redhat.com> + */ + +#include <config.h> + +#include "domain_capabilities.h" +#include "domain_conf.h" +#include "viralloc.h" +#include "virstring.h" + +#define VIR_FROM_THIS VIR_FROM_CAPABILITIES + +static virClassPtr virDomainCapsClass; + +static void virDomainCapsDispose(void *obj); + +static int virDomainCapsOnceInit(void) +{ + if (!(virDomainCapsClass = virClassNew(virClassForObjectLockable(), + "virDomainCapsClass", + sizeof(virDomainCaps), + virDomainCapsDispose))) + return -1; + return 0; +} + + +VIR_ONCE_GLOBAL_INIT(virDomainCaps) + + +static void +virDomainCapsDispose(void *obj) +{ + virDomainCapsPtr caps = obj; + + VIR_FREE(caps->path); + VIR_FREE(caps->machine); +} + + +virDomainCapsPtr +virDomainCapsNew(const char *path, + const char *machine, + virArch arch, + virDomainVirtType virttype) +{ + virDomainCapsPtr caps = NULL; + + if (virDomainCapsInitialize() < 0) + return NULL; + + if (!(caps = virObjectLockableNew(virDomainCapsClass))) + return NULL; + + if (VIR_STRDUP(caps->path, path) < 0 || + VIR_STRDUP(caps->machine, machine) < 0) + goto error; + caps->arch = arch; + caps->virttype = virttype; + + return caps; + error: + virObjectUnref(caps); + return NULL; +} + + +int +virDomainCapsEnumSet(virDomainCapsEnumPtr capsEnum, + const char *capsEnumName, + size_t nvalues, + unsigned int *values) +{ + int ret = -1; + size_t i; + + for (i = 0; i < nvalues; i++) { + unsigned int val = 1 << values[i]; + + if (!val) { + /* Integer overflow */ + virReportError(VIR_ERR_INTERNAL_ERROR, + _("integer overflow on %s. Please contact the " + "libvirt development team at libvir-list@redhat.com"), + capsEnumName); + goto cleanup; + } + + capsEnum->values |= val; + } + + ret = 0; + cleanup: + return ret; +} + + +void +virDomainCapsEnumClear(virDomainCapsEnumPtr capsEnum) +{ + capsEnum->values = 0; +} + + +static int +virDomainCapsEnumFormat(virBufferPtr buf, + virDomainCapsEnumPtr capsEnum, + const char *capsEnumName, + virDomainCapsValToStr valToStr) +{ + int ret = -1; + size_t i; + + virBufferAsprintf(buf, "<enum name='%s'", capsEnumName); + if (!capsEnum->values) { + virBufferAddLit(buf, "/>\n"); + ret = 0; + goto cleanup; + } + virBufferAddLit(buf, ">\n"); + virBufferAdjustIndent(buf, 2); + + for (i = 0; i < sizeof(capsEnum->values) * CHAR_BIT; i++) { + const char *val; + + if (!(capsEnum->values & (1 << i))) + continue; + + if ((val = (valToStr)(i))) + virBufferAsprintf(buf, "<value>%s</value>\n", val); + } + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</enum>\n"); + + ret = 0; + cleanup: + return ret; +} + +#define FORMAT_PROLOGUE(item) \ + do { \ + virBufferAsprintf(buf, "<" #item " supported='%s'%s\n", \ + item->device.supported ? "yes" : "no", \ + item->device.supported ? ">" : "/>"); \ + if (!item->device.supported) \ + return; \ + virBufferAdjustIndent(buf, 2); \ + } while (0) + +#define FORMAT_EPILOGUE(item) \ + do { \ + virBufferAdjustIndent(buf, -2); \ + virBufferAddLit(buf, "</" #item ">\n"); \ + } while (0) + +#define ENUM_PROCESS(master, capsEnum, valToStr) \ + do { \ + virDomainCapsEnumFormat(buf, &master->capsEnum, \ + #capsEnum, valToStr); \ + } while (0) + +static void +virDomainCapsDeviceDiskFormat(virBufferPtr buf, + virDomainCapsDeviceDiskPtr const disk) +{ + FORMAT_PROLOGUE(disk); + + ENUM_PROCESS(disk, diskDevice, virDomainDiskDeviceTypeToString); + ENUM_PROCESS(disk, bus, virDomainDiskBusTypeToString); + + FORMAT_EPILOGUE(disk); +} + + +static void +virDomainCapsDeviceHostdevFormat(virBufferPtr buf, + virDomainCapsDeviceHostdevPtr const hostdev) +{ + FORMAT_PROLOGUE(hostdev); + + ENUM_PROCESS(hostdev, mode, virDomainHostdevModeTypeToString); + ENUM_PROCESS(hostdev, startupPolicy, virDomainStartupPolicyTypeToString); + ENUM_PROCESS(hostdev, subsysType, virDomainHostdevSubsysTypeToString); + ENUM_PROCESS(hostdev, capsType, virDomainHostdevCapsTypeToString); + ENUM_PROCESS(hostdev, pciBackend, virDomainHostdevSubsysPCIBackendTypeToString); + + FORMAT_EPILOGUE(hostdev); +} + + +static int +virDomainCapsFormatInternal(virBufferPtr buf, + virDomainCapsPtr const caps) +{ + const char *virttype_str = virDomainVirtTypeToString(caps->virttype); + const char *arch_str = virArchToString(caps->arch); + + virBufferAddLit(buf, "<domainCapabilities>\n"); + virBufferAdjustIndent(buf, 2); + + virBufferAsprintf(buf, "<path>%s</path>\n", caps->path); + virBufferAsprintf(buf, "<domain>%s</domain>\n", virttype_str);
<virttype>?
+ virBufferAsprintf(buf, "<machine>%s</machine>\n", caps->machine); + virBufferAsprintf(buf, "<arch>%s</arch>\n", arch_str); + + if (caps->maxvcpus) + virBufferAsprintf(buf, "<vcpu max='%d'/>\n", caps->maxvcpus); + + virBufferAddLit(buf, "<devices>\n"); + virBufferAdjustIndent(buf, 2); + + virDomainCapsDeviceDiskFormat(buf, &caps->disk); + virDomainCapsDeviceHostdevFormat(buf, &caps->hostdev); + + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</devices>\n"); + + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</domainCapabilities>\n"); + return 0; +} + + +char * +virDomainCapsFormat(virDomainCapsPtr const caps) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + + if (virDomainCapsFormatInternal(&buf, caps) < 0) { + virBufferFreeAndReset(&buf); + return NULL; + } + + return virBufferContentAndReset(&buf); +} diff --git a/src/conf/domain_capabilities.h b/src/conf/domain_capabilities.h new file mode 100644 index 0000000..731e66f --- /dev/null +++ b/src/conf/domain_capabilities.h @@ -0,0 +1,103 @@ +/* + * domain_capabilities.h: domain capabilities XML processing + * + * Copyright (C) 2014 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Author: Michal Privoznik <mprivozn@redhat.com> + */ + +#ifndef __DOMAIN_CAPABILITIES_H__ +# define __DOMAIN_CAPABILITIES_H__ + +# include "internal.h" +# include "domain_conf.h" + +typedef const char * (*virDomainCapsValToStr)(int value); + +typedef struct _virDomainCaps virDomainCaps; +typedef virDomainCaps *virDomainCapsPtr; + +typedef struct _virDomainCapsEnum virDomainCapsEnum; +typedef virDomainCapsEnum *virDomainCapsEnumPtr; +struct _virDomainCapsEnum { + unsigned int values; /* Bitmask of values supported in the corresponding enum */ +}; + +typedef struct _virDomainCapsDevice virDomainCapsDevice; +typedef virDomainCapsDevice *virDomainCapsDevicePtr; +struct _virDomainCapsDevice { + bool supported; /* true if <devtype> is supported by hypervisor */ +}; + +typedef struct _virDomainCapsDeviceDisk virDomainCapsDeviceDisk; +typedef virDomainCapsDeviceDisk *virDomainCapsDeviceDiskPtr; +struct _virDomainCapsDeviceDisk { + virDomainCapsDevice device; + virDomainCapsEnum diskDevice; /* Info about virDomainDiskDevice enum values */ + virDomainCapsEnum bus; /* Info about virDomainDiskBus enum values */ + /* add new fields here */ +}; + +typedef struct _virDomainCapsDeviceHostdev virDomainCapsDeviceHostdev; +typedef virDomainCapsDeviceHostdev *virDomainCapsDeviceHostdevPtr; +struct _virDomainCapsDeviceHostdev { + virDomainCapsDevice device; + virDomainCapsEnum mode; /* Info about virDomainHostdevMode */ + virDomainCapsEnum startupPolicy; /* Info about virDomainStartupPolicy */ + virDomainCapsEnum subsysType; /* Info about virDomainHostdevSubsysType */ + virDomainCapsEnum capsType; /* Info about virDomainHostdevCapsType */ + virDomainCapsEnum pciBackend; /* Info about virDomainHostdevSubsysPCIBackendType */ + /* add new fields here */ +};
Hmm... well maybe I should have read further into the data structures before making too many comments in the html section :-) It seems initially it's a very bare bones set of capabilities output. I guess I had just assumed there was going to be some fun typedef magic using the Type{To|From}String API's in order to populate the fields. This is going to be a fairly manual process of adding new elements, modifying the html, modifying the code, etc.
+ +struct _virDomainCaps { + virObjectLockable parent; + + char *path; /* path to emulator binary */ + virDomainVirtType virttype; /* virtualization type */ + char *machine; /* machine type */ + virArch arch; /* domain architecture */ + + /* Some machine specific info */ + int maxvcpus; + + virDomainCapsDeviceDisk disk; + virDomainCapsDeviceHostdev hostdev; + /* add new domain devices here */ +}; + +virDomainCapsPtr virDomainCapsNew(const char *path, + const char *machine, + virArch arch, + virDomainVirtType virttype); + +# define VIR_DOMAIN_CAPS_ENUM_SET(capsEnum, ...) \ + do { \ + unsigned int __values[] = {__VA_ARGS__}; \ + size_t __nvalues = ARRAY_CARDINALITY(__values); \ + virDomainCapsEnumSet(&(capsEnum), #capsEnum, \ + __nvalues, __values); \ + } while (0) + +int virDomainCapsEnumSet(virDomainCapsEnumPtr capsEnum, + const char *capsEnumName, + size_t nvalues, + unsigned int *values); +void virDomainCapsEnumClear(virDomainCapsEnumPtr capsEnum); + +char * virDomainCapsFormat(virDomainCapsPtr const caps); +#endif /* __DOMAIN_CAPABILITIES_H__ */ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 1e1dd84..40585bd 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -130,6 +130,13 @@ virDomainAuditStop; virDomainAuditVcpu;
+# conf/domain_capabilities.h +virDomainCapsEnumClear; +virDomainCapsEnumSet; +virDomainCapsFormat; +virDomainCapsNew; + + # conf/domain_conf.h virBlkioDeviceArrayClear; virDiskNameToBusDeviceIndex; diff --git a/tests/Makefile.am b/tests/Makefile.am index 025b847..97af0d9 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -74,6 +74,8 @@ EXTRA_DIST = \ commanddata \ confdata \ cputestdata \ + domaincapsschemadata \ + domaincapsschematest \ domainconfdata \ domainschemadata \ domainschematest \ @@ -167,6 +169,7 @@ test_programs = virshtest sockettest \ virnetdevbandwidthtest \ virkmodtest \ vircapstest \ + domaincapstest \ domainconftest \ virhostdevtest \ vircaps2xmltest \ @@ -318,6 +321,7 @@ test_scripts = \ networkschematest \ storagepoolschematest \ storagevolschematest \ + domaincapsschematest \ domainschematest \ nodedevschematest \ nwfilterschematest \ @@ -827,6 +831,10 @@ vircaps2xmltest_SOURCES = \ vircaps2xmltest.c testutils.h testutils.c vircaps2xmltest_LDADD = $(LDADDS)
+domaincapstest_SOURCES = \ + domaincapstest.c testutils.h testutils.c +domaincapstest_LDADD = $(LDADDS) + if WITH_LIBVIRTD libvirtdconftest_SOURCES = \ libvirtdconftest.c testutils.h testutils.c \ diff --git a/tests/domaincapsschemadata/domaincaps-basic.xml b/tests/domaincapsschemadata/domaincaps-basic.xml new file mode 100644 index 0000000..9963519 --- /dev/null +++ b/tests/domaincapsschemadata/domaincaps-basic.xml @@ -0,0 +1,10 @@ +<domainCapabilities> + <path>/bin/emulatorbin</path> + <domain>uml</domain> + <machine>my-machine-type</machine> + <arch>x86_64</arch> + <devices> + <disk supported='no'/> + <hostdev supported='no'/> + </devices> +</domainCapabilities> diff --git a/tests/domaincapsschemadata/domaincaps-full.xml b/tests/domaincapsschemadata/domaincaps-full.xml new file mode 100644 index 0000000..58dd4cb --- /dev/null +++ b/tests/domaincapsschemadata/domaincaps-full.xml @@ -0,0 +1,56 @@ +<domainCapabilities> + <path>/bin/emulatorbin</path> + <domain>kvm</domain> + <machine>my-machine-type</machine> + <arch>x86_64</arch> + <vcpu max='255'/> + <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>xen</value> + <value>usb</value> + <value>uml</value> + <value>sata</value> + <value>sd</value> + </enum> + </disk> + <hostdev supported='yes'> + <enum name='mode'> + <value>subsystem</value> + <value>capabilities</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'> + <value>storage</value> + <value>misc</value> + <value>net</value> + </enum> + <enum name='pciBackend'> + <value>default</value> + <value>kvm</value> + <value>vfio</value> + <value>xen</value> + </enum> + </hostdev> + </devices> +</domainCapabilities> diff --git a/tests/domaincapsschematest b/tests/domaincapsschematest new file mode 100755 index 0000000..9baf44a --- /dev/null +++ b/tests/domaincapsschematest @@ -0,0 +1,11 @@ +#!/bin/sh + +: ${srcdir=.} +. $srcdir/test-lib.sh +. $abs_srcdir/schematestutils.sh + +DIRS="" +DIRS="$DIRS domaincapsschemadata" +SCHEMA="domaincaps.rng" + +check_schema "$DIRS" "$SCHEMA" diff --git a/tests/domaincapstest.c b/tests/domaincapstest.c new file mode 100644 index 0000000..6cdd086 --- /dev/null +++ b/tests/domaincapstest.c @@ -0,0 +1,149 @@ +/* + * Copyright (C) Red Hat, Inc. 2014 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Authors: + * Michal Privoznik <mprivozn@redhat.com> + */ + +#include <config.h> +#include <stdlib.h> + +#include "testutils.h" +#include "domain_capabilities.h" + + +#define VIR_FROM_THIS VIR_FROM_NONE + +typedef void (*virDomainCapsFill)(virDomainCapsPtr domCaps, + void *opaque); + +#define SET_ALL_BITS(x) \ + memset(&(x.values), 0xff, sizeof(x.values)) + +static void +fillAll(virDomainCapsPtr domCaps, + void *opaque ATTRIBUTE_UNUSED) +{ + virDomainCapsDeviceDiskPtr disk = &domCaps->disk; + virDomainCapsDeviceHostdevPtr hostdev = &domCaps->hostdev; + domCaps->maxvcpus = 255; + + disk->device.supported = true; + SET_ALL_BITS(disk->diskDevice); + SET_ALL_BITS(disk->bus); + + hostdev->device.supported = true; + SET_ALL_BITS(hostdev->mode); + SET_ALL_BITS(hostdev->startupPolicy); + SET_ALL_BITS(hostdev->subsysType); + SET_ALL_BITS(hostdev->capsType); + SET_ALL_BITS(hostdev->pciBackend); +} + +static virDomainCapsPtr +buildVirDomainCaps(const char *emulatorbin, + const char *machine, + virArch arch, + virDomainVirtType type, + virDomainCapsFill fillFunc, + void *opaque) +{ + virDomainCapsPtr domCaps; + + if (!(domCaps = virDomainCapsNew(emulatorbin, machine, arch, type))) + goto cleanup; + + if (fillFunc) + fillFunc(domCaps, opaque); + + cleanup: + return domCaps; +} + +struct test_virDomainCapsFormatData { + const char *filename; + const char *emulatorbin; + const char *machine; + virArch arch; + virDomainVirtType type; + virDomainCapsFill fillFunc; + void *opaque; +}; + +static int +test_virDomainCapsFormat(const void *opaque) +{ + struct test_virDomainCapsFormatData *data = + (struct test_virDomainCapsFormatData *) opaque; + virDomainCapsPtr domCaps = NULL; + char *path = NULL; + char *domCapsXML = NULL; + char *domCapsFromFile = NULL; + int ret = -1; + + if (virAsprintf(&path, "%s/domaincapsschemadata/domaincaps-%s.xml", + abs_srcdir, data->filename) < 0) + goto cleanup; + + if (virFileReadAll(path, 8192, &domCapsFromFile) < 0) + goto cleanup; + + if (!(domCaps = buildVirDomainCaps(data->emulatorbin, data->machine, + data->arch, data->type, + data->fillFunc, data->opaque))) + goto cleanup; + + if (!(domCapsXML = virDomainCapsFormat(domCaps))) + goto cleanup; + + if (STRNEQ(domCapsFromFile, domCapsXML)) { + virtTestDifference(stderr, domCapsFromFile, domCapsXML); + goto cleanup; + } + + ret = 0; + cleanup: + VIR_FREE(domCapsFromFile); + VIR_FREE(domCapsXML); + VIR_FREE(path); + virObjectUnref(domCaps); + return ret; +} + +static int +mymain(void) +{ + int ret = 0; + +#define DO_TEST(Filename, Emulatorbin, Machine, Arch, Type, ...) \ + do { \ + struct test_virDomainCapsFormatData data = {.filename = Filename, \ + .emulatorbin = Emulatorbin, .machine = Machine, .arch = Arch, \ + .type = Type, __VA_ARGS__}; \ + if (virtTestRun(Filename, test_virDomainCapsFormat, &data) < 0) \ + ret = -1; \ + } while (0) + + DO_TEST("basic", "/bin/emulatorbin", "my-machine-type", + VIR_ARCH_X86_64, VIR_DOMAIN_VIRT_UML); + DO_TEST("full", "/bin/emulatorbin", "my-machine-type", + VIR_ARCH_X86_64, VIR_DOMAIN_VIRT_KVM, .fillFunc = fillAll); + + return ret; +} + +VIRT_TEST_MAIN(mymain)

On Wed, Jul 02, 2014 at 10:32:59AM -0400, John Ferlan wrote:
I was trying to envision use cases - that is how is this expected to be used and what "knowledge" is assumed of the caller/user vs. being designed to a more naive user trying to glean information about what's available. You have a very specific use case described - can I determine if vfio is supported, but that requires someone knowing quite a bit of information that isn't easily accessible unless you read sources or have a bit of history built up.
For the domcapabilities command that eventually gets added - how does one know what to provide for the 4 options without knowing a bit about the environment. It seems the assumption is the user knows to pass certain elements.
The 'virttype' is pretty easy - that comes from the connection - so I wonder why it's a parameter to be provided. Does one really have to have a connection to get the data?
The 'emulatorbin' is less obvious. If it's not passed, there is a way to get the default value given that you have a virttype, an os type, and an os arch using virCapabilitiesDefaultGuestEmulator(). What if someone provides "/usr/bin/qemu-kvm" or "/usr/libexec/qemu-kvm" or is there an expectation of /usr/bin/qemu-system-x86_64?
The 'arch' requires a bit more knowledge, but is certainly "obtainable" as a default by the current host arch, right? There's also virCapabilitiesDefaultGuestArch(). However, if someone was looking to find out what was running on the remote connection (not the local machine), then that assumption would be incorrect. Seems we should be able to figure out what arch is associated with the connection.
I think 'machine' is perhaps the most odd to provide; however, like arch and emulatorbin, there is virCapabilitiesDefaultGuestMachine() to help you out. For this if one passed "pc" does that work - or do they have to pass something like "pc-i440fx-1.6" with the next question being how would they know to generate that?
The valid values for all those parameters are listed in the main <capabilities> XML under the <guest> sections. The app is of course free to ask about support for other non-listed values if they have a custom emulator binary they're passing in. Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On Wed, Jul 02, 2014 at 10:32:59AM -0400, John Ferlan wrote:
diff --git a/docs/formatdomaincaps.html.in b/docs/formatdomaincaps.html.in new file mode 100644 index 0000000..cfd61d9 --- /dev/null +++ b/docs/formatdomaincaps.html.in
Since there's a "Capabilities" page describing the Driver (Host/Guest) Capabilities and now this one for Domain - do you see a future need for storage, network, etc. type capabilities? If so, other than the "volume of data" then why not extend capabilities so that it could list everything available for everything we know about? e.g.:
<capabilities> <host... /> <guest... /> <domain... /> <storage... /> <network... /> <interface... /> </capabilities>
Architecturally, does it make sense to merge them or keep then separate? There seems to be a relationship between them. Furthermore, if the goal is to just provide information, then using one pile of xml output to store everything may be useful to someone. For the more seasoned individual using a specific/directed API that is planned will provide the directed answer. I think the 'virsh capabilies' output certainly shows it's possible to grab/use the various arch, machine, emulator, and domain type values to generate lengthy output listing every possible option.
First we want the <capabilities> XML to keep a reasonable size. If we listed everything in that one document, it would end up being many many MB in size. Second, the capabilities XML can only provide info on the default emulators known to libvirt. The separate API lets us get info on non-default emulator binaries too. To your original question though, I do see us adding more APIs in the future to query info about storage & networking. Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 02.07.2014 16:32, John Ferlan wrote:
On 06/30/2014 11:31 AM, Michal Privoznik wrote:
This new module holds and formats capabilities for emulator. If you are about to create a new domain, you may want to know what is the host or hypervisor capable of. To make sure we don't regress on the XML, the formatting is not something left for each driver to implement, rather there's general format function.
The domain capabilities is a lockable object (even though the locking is not necessary yet) which uses reference counter.
I started looking at these yesterday, but ended up having more questions than I originally thought I would, so perhaps better to get some answered. Of course it's already too wordy (sorry)...
I was trying to envision use cases - that is how is this expected to be used and what "knowledge" is assumed of the caller/user vs. being designed to a more naive user trying to glean information about what's available. You have a very specific use case described - can I determine if vfio is supported, but that requires someone knowing quite a bit of information that isn't easily accessible unless you read sources or have a bit of history built up.
Well, that's only for start. Rich already requested some extensions which will be a follow up patches.
For the domcapabilities command that eventually gets added - how does one know what to provide for the 4 options without knowing a bit about the environment. It seems the assumption is the user knows to pass certain elements.
The 'virttype' is pretty easy - that comes from the connection - so I wonder why it's a parameter to be provided. Does one really have to have a connection to get the data?
Consider 'kvm' vs 'qemu'. Or more verbose: <domain type='kvm'/> vs <domain type='qemu'/> Both of these are served by qemu:///{system,session} but cannot be guessed from the connection URI.
The 'emulatorbin' is less obvious. If it's not passed, there is a way to get the default value given that you have a virttype, an os type, and an os arch using virCapabilitiesDefaultGuestEmulator(). What if someone provides "/usr/bin/qemu-kvm" or "/usr/libexec/qemu-kvm" or is there an expectation of /usr/bin/qemu-system-x86_64?
Yes, there are several possibilities. Either you'll pass emulatorbin (which corresponds to <emulator/> in domain XML) or it's defualt value is guessed from the rest of the arguments. But this is how it works in qemu. In other drivers it may work differently (once implemented).
The 'arch' requires a bit more knowledge, but is certainly "obtainable" as a default by the current host arch, right? There's also virCapabilitiesDefaultGuestArch(). However, if someone was looking to find out what was running on the remote connection (not the local machine), then that assumption would be incorrect. Seems we should be able to figure out what arch is associated with the connection.
I don't think so. You can run ARM guest on x86_64 machine (where the connection is to the x86_64 machine and you are querying capabilities for ARM emulation).
I think 'machine' is perhaps the most odd to provide; however, like arch and emulatorbin, there is virCapabilitiesDefaultGuestMachine() to help you out. For this if one passed "pc" does that work - or do they have to pass something like "pc-i440fx-1.6" with the next question being how would they know to generate that?
Again, this is something that users may provide, or they can let libvirt to fill in the sane default. Consider if you want to run, e.g. 'isapc' (yeah, why would anybody do that? But that's a different story :P).
Reading the domain_conf code - only 'virttype' and 'os_type' really seem to be required - everything else can be figured out "by default" given the two.
Correct. That's why the client side of the API checks only for the valid connection and the rest of the checks is left for drivers to implement, since (in general) different drivers have different sets of minimal arguments required. Having (a) virsh command(s) to display possible options may be
a nice addition, especially for the naive user... Should be very easy to add something that could print out the virArch options. Leaving only the need to know what the os_type is before behing able to at least fetch defaults and generate XML output. Perhaps someone just using "virsh domcapabilities" would print out tables of all arch's.. Similarly if arch was provided, then print out all emulatorbin's (if that's possible) for the arch or just the default emulatorbin... Given an arch and emulatorbin, then print out the machines available.
Sure, virsh user friendliness is not something I'm so proud of. But I have nothing else to say that "patches are welcome". I mean, there are other commands that take string values that only XML struct aware user knows about: attach-disk is rich of examples (--driver, --cache, ...) Michal

On 07/02/2014 12:07 PM, Michal Privoznik wrote:
On 02.07.2014 16:32, John Ferlan wrote:
On 06/30/2014 11:31 AM, Michal Privoznik wrote:
This new module holds and formats capabilities for emulator. If you are about to create a new domain, you may want to know what is the host or hypervisor capable of. To make sure we don't regress on the XML, the formatting is not something left for each driver to implement, rather there's general format function.
The domain capabilities is a lockable object (even though the locking is not necessary yet) which uses reference counter.
I started looking at these yesterday, but ended up having more questions than I originally thought I would, so perhaps better to get some answered. Of course it's already too wordy (sorry)...
I was trying to envision use cases - that is how is this expected to be used and what "knowledge" is assumed of the caller/user vs. being designed to a more naive user trying to glean information about what's available. You have a very specific use case described - can I determine if vfio is supported, but that requires someone knowing quite a bit of information that isn't easily accessible unless you read sources or have a bit of history built up.
Well, that's only for start. Rich already requested some extensions which will be a follow up patches.
I saw those in the original RFC posting and I see Dan updated the bz to point at the RFC that resulted in this series...
For the domcapabilities command that eventually gets added - how does one know what to provide for the 4 options without knowing a bit about the environment. It seems the assumption is the user knows to pass certain elements.
The 'virttype' is pretty easy - that comes from the connection - so I wonder why it's a parameter to be provided. Does one really have to have a connection to get the data?
Consider 'kvm' vs 'qemu'. Or more verbose:
<domain type='kvm'/> vs <domain type='qemu'/>
Both of these are served by qemu:///{system,session} but cannot be guessed from the connection URI.
oh yeah - right... And if I supply "kvm" or "qemu", both will use the qemu_driver.c code to resolve defaults for emulatorbin, arch, and machine right? To that degree they're synonyms. It's only when filling in the details of the domaincaps from qemucaps does it matter.
The 'emulatorbin' is less obvious. If it's not passed, there is a way to get the default value given that you have a virttype, an os type, and an os arch using virCapabilitiesDefaultGuestEmulator(). What if someone provides "/usr/bin/qemu-kvm" or "/usr/libexec/qemu-kvm" or is there an expectation of /usr/bin/qemu-system-x86_64?
Yes, there are several possibilities. Either you'll pass emulatorbin (which corresponds to <emulator/> in domain XML) or it's defualt value is guessed from the rest of the arguments. But this is how it works in qemu. In other drivers it may work differently (once implemented).
Right - understood it's in the domain, but if you don't have a domain, you still may not know what to provide. The qemu driver code expects you to pass either "arch" or "emulator" in order to provide anything. So you have to know one or the other to get something and the only place to get that would then be the virsh capabilities output which I figured out after I'd written most of the original response.
The 'arch' requires a bit more knowledge, but is certainly "obtainable" as a default by the current host arch, right? There's also virCapabilitiesDefaultGuestArch(). However, if someone was looking to find out what was running on the remote connection (not the local machine), then that assumption would be incorrect. Seems we should be able to figure out what arch is associated with the connection.
I don't think so. You can run ARM guest on x86_64 machine (where the connection is to the x86_64 machine and you are querying capabilities for ARM emulation).
So again - you have to know emulator or arch for qemu and that is fetched from virsh capabilities - something that could be at least documented. That is - how to figure what to pass to virsh domcapabilities... (something that gets more difficult to type every time I type it :-) - "domcaps" is so much easier...)
I think 'machine' is perhaps the most odd to provide; however, like arch and emulatorbin, there is virCapabilitiesDefaultGuestMachine() to help you out. For this if one passed "pc" does that work - or do they have to pass something like "pc-i440fx-1.6" with the next question being how would they know to generate that?
Again, this is something that users may provide, or they can let libvirt to fill in the sane default. Consider if you want to run, e.g. 'isapc' (yeah, why would anybody do that? But that's a different story :P).
Right and it's not clear what has to be provided until one runs the command for the 'virttype'...
Reading the domain_conf code - only 'virttype' and 'os_type' really seem to be required - everything else can be figured out "by default" given the two.
Correct. That's why the client side of the API checks only for the valid connection and the rest of the checks is left for drivers to implement, since (in general) different drivers have different sets of minimal arguments required.
The piece of the puzzle that's missing from the descriptions - user/customer has to start somewhere though.
Having (a) virsh command(s) to display possible options may be
a nice addition, especially for the naive user... Should be very easy to add something that could print out the virArch options. Leaving only the need to know what the os_type is before behing able to at least fetch defaults and generate XML output. Perhaps someone just using "virsh domcapabilities" would print out tables of all arch's.. Similarly if arch was provided, then print out all emulatorbin's (if that's possible) for the arch or just the default emulatorbin... Given an arch and emulatorbin, then print out the machines available.
Sure, virsh user friendliness is not something I'm so proud of. But I have nothing else to say that "patches are welcome". I mean, there are other commands that take string values that only XML struct aware user knows about: attach-disk is rich of examples (--driver, --cache, ...)
Right/Understood - there are certainly other examples where figuring out the right incantation to use can be a challenge. This change is like a gift that keeps giving in order to hopefully one day catch up to everything that's currently supported and then "require it" (by some rule) for anything added. The series does scratch an itch to solve a specific problem - knowing whether vfio is supported or not for a specific virttype, arch, machine, and emulator, but it also opens Pandora's box of never ending requests to display some specific feature someone is interested in. Yes, like you note - patches are welcome... Still need to understand the vision so that patches are useful... For instance if a "--list" option was added to essentially print everything that we knew about based on what was in virsh capabilities... I'm not opposed to the patches and you have Dan's ACK - my only hope is a clearer understanding of what's there now and what's expected. Documenting that using the virsh capabilities output to find the various values wasn't clear to me until I started poking around. I don't mind helping with the language/documentation aspects of it - just want to be sure I have a better picture of what I'm trying to describe! John

The API should expose the information contained in virDomainCapsPtr. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- include/libvirt/libvirt.h.in | 7 ++++++ src/driver.h | 9 ++++++++ src/libvirt.c | 52 ++++++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 5 +++++ src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 19 +++++++++++++++- src/remote_protocol-structs | 11 ++++++++++ 7 files changed, 103 insertions(+), 1 deletion(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 032d6e6..aedd49a 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1585,6 +1585,13 @@ int virNodeGetInfo (virConnectPtr conn, virNodeInfoPtr info); char * virConnectGetCapabilities (virConnectPtr conn); +char * virConnectGetDomainCapabilities(virConnectPtr conn, + const char *emulatorbin, + const char *arch, + const char *machine, + const char *virttype, + unsigned int flags); + int virNodeGetCPUStats (virConnectPtr conn, int cpuNum, virNodeCPUStatsPtr params, diff --git a/src/driver.h b/src/driver.h index 5018068..c769675 100644 --- a/src/driver.h +++ b/src/driver.h @@ -126,6 +126,14 @@ typedef int typedef char * (*virDrvConnectGetCapabilities)(virConnectPtr conn); +typedef char * +(*virDrvConnectGetDomainCapabilities)(virConnectPtr conn, + const char *emulatorbin, + const char *arch, + const char *machine, + const char *virttype, + unsigned int flags); + typedef int (*virDrvConnectListDomains)(virConnectPtr conn, int *ids, @@ -1407,6 +1415,7 @@ struct _virDriver { virDrvDomainGetTime domainGetTime; virDrvDomainSetTime domainSetTime; virDrvNodeGetFreePages nodeGetFreePages; + virDrvConnectGetDomainCapabilities connectGetDomainCapabilities; }; diff --git a/src/libvirt.c b/src/libvirt.c index 88c1f49..3511b1b 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -21137,3 +21137,55 @@ virNetworkDHCPLeaseFree(virNetworkDHCPLeasePtr lease) VIR_FREE(lease->clientid); VIR_FREE(lease); } + +/** + * virConnectGetDomainCapabilities: + * @conn: pointer to the hypervisor connection + * @emulatorbin: path to emulator + * @arch: domain architecture + * @machine: machine type + * @virttype: virtualization type + * @flags: extra flags; not used yet, so callers should always pass 0 + * + * Prior creating a domain (for instance via virDomainCreateXML + * or virDomainDefineXML) it may be suitable to know what the + * underlying emulator and/or libvirt is capable of. For + * instance, if host, libvirt and qemu is capable of VFIO + * passthrough and so on. + * + * Returns NULL in case of error, or an XML string + * defining the capabilities. + */ +char * +virConnectGetDomainCapabilities(virConnectPtr conn, + const char *emulatorbin, + const char *arch, + const char *machine, + const char *virttype, + unsigned int flags) +{ + VIR_DEBUG("conn=%p, emulatorbin=%s, arch=%s, " + "machine=%s, virttype=%s, flags=%x", + conn, emulatorbin, arch, machine, virttype, flags); + + virResetLastError(); + + virCheckConnectReturn(conn, NULL); + + if (conn->driver->connectGetDomainCapabilities) { + char *ret; + ret = conn->driver->connectGetDomainCapabilities(conn, emulatorbin, + arch, machine, + virttype, flags); + if (!ret) + goto error; + VIR_DEBUG("conn=%p, ret=%s", conn, ret); + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(conn); + return NULL; +} diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 65a5b43..9f4016a 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -665,4 +665,9 @@ LIBVIRT_1.2.6 { virNetworkGetDHCPLeases; } LIBVIRT_1.2.5; +LIBVIRT_1.2.7 { + global: + virConnectGetDomainCapabilities; +} LIBVIRT_1.2.6; + # .... define new API here using predicted next version number .... diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 3c10d5c..88fc977 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -8004,6 +8004,7 @@ static virDriver remote_driver = { .domainGetTime = remoteDomainGetTime, /* 1.2.5 */ .domainSetTime = remoteDomainSetTime, /* 1.2.5 */ .nodeGetFreePages = remoteNodeGetFreePages, /* 1.2.6 */ + .connectGetDomainCapabilities = remoteConnectGetDomainCapabilities, /* 1.2.7 */ }; static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index bff2c47..5c316fb 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -478,6 +478,18 @@ struct remote_connect_get_capabilities_ret { remote_nonnull_string capabilities; }; +struct remote_connect_get_domain_capabilities_args { + remote_string emulatorbin; + remote_string arch; + remote_string machine; + remote_string virttype; + unsigned int flags; +}; + +struct remote_connect_get_domain_capabilities_ret { + remote_nonnull_string capabilities; +}; + struct remote_node_get_cpu_stats_args { int cpuNum; int nparams; @@ -5402,6 +5414,11 @@ enum remote_procedure { * @generate: none * @acl: network:read */ - REMOTE_PROC_NETWORK_GET_DHCP_LEASES = 341 + REMOTE_PROC_NETWORK_GET_DHCP_LEASES = 341, + /** + * @generate: both + * @acl: connect:write + */ + REMOTE_PROC_CONNECT_GET_DOMAIN_CAPABILITIES = 342 }; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index a14e1fd..9bf09b8 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -148,6 +148,16 @@ struct remote_node_get_info_ret { struct remote_connect_get_capabilities_ret { remote_nonnull_string capabilities; }; +struct remote_connect_get_domain_capabilities_args { + remote_string emulatorbin; + remote_string arch; + remote_string machine; + remote_string virttype; + u_int flags; +}; +struct remote_connect_get_domain_capabilities_ret { + remote_nonnull_string capabilities; +}; struct remote_node_get_cpu_stats_args { int cpuNum; int nparams; @@ -2851,4 +2861,5 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_EVENT_BLOCK_JOB_2 = 339, REMOTE_PROC_NODE_GET_FREE_PAGES = 340, REMOTE_PROC_NETWORK_GET_DHCP_LEASES = 341, + REMOTE_PROC_CONNECT_GET_DOMAIN_CAPABILITIES = 342, }; -- 1.8.5.5

On Mon, Jun 30, 2014 at 05:31:45PM +0200, Michal Privoznik wrote:
The API should expose the information contained in virDomainCapsPtr.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- include/libvirt/libvirt.h.in | 7 ++++++ src/driver.h | 9 ++++++++ src/libvirt.c | 52 ++++++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 5 +++++ src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 19 +++++++++++++++- src/remote_protocol-structs | 11 ++++++++++ 7 files changed, 103 insertions(+), 1 deletion(-)
ACK Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 06/30/2014 11:31 AM, Michal Privoznik wrote:
The API should expose the information contained in virDomainCapsPtr.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- include/libvirt/libvirt.h.in | 7 ++++++ src/driver.h | 9 ++++++++ src/libvirt.c | 52 ++++++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 5 +++++ src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 19 +++++++++++++++- src/remote_protocol-structs | 11 ++++++++++ 7 files changed, 103 insertions(+), 1 deletion(-)
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 032d6e6..aedd49a 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1585,6 +1585,13 @@ int virNodeGetInfo (virConnectPtr conn, virNodeInfoPtr info); char * virConnectGetCapabilities (virConnectPtr conn);
+char * virConnectGetDomainCapabilities(virConnectPtr conn, + const char *emulatorbin, + const char *arch, + const char *machine, + const char *virttype, + unsigned int flags); + int virNodeGetCPUStats (virConnectPtr conn, int cpuNum, virNodeCPUStatsPtr params, diff --git a/src/driver.h b/src/driver.h index 5018068..c769675 100644 --- a/src/driver.h +++ b/src/driver.h @@ -126,6 +126,14 @@ typedef int typedef char * (*virDrvConnectGetCapabilities)(virConnectPtr conn);
+typedef char * +(*virDrvConnectGetDomainCapabilities)(virConnectPtr conn, + const char *emulatorbin, + const char *arch, + const char *machine, + const char *virttype, + unsigned int flags); + typedef int (*virDrvConnectListDomains)(virConnectPtr conn, int *ids, @@ -1407,6 +1415,7 @@ struct _virDriver { virDrvDomainGetTime domainGetTime; virDrvDomainSetTime domainSetTime; virDrvNodeGetFreePages nodeGetFreePages; + virDrvConnectGetDomainCapabilities connectGetDomainCapabilities; };
diff --git a/src/libvirt.c b/src/libvirt.c index 88c1f49..3511b1b 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -21137,3 +21137,55 @@ virNetworkDHCPLeaseFree(virNetworkDHCPLeasePtr lease) VIR_FREE(lease->clientid); VIR_FREE(lease); } + +/** + * virConnectGetDomainCapabilities: + * @conn: pointer to the hypervisor connection + * @emulatorbin: path to emulator + * @arch: domain architecture + * @machine: machine type + * @virttype: virtualization type + * @flags: extra flags; not used yet, so callers should always pass 0 + * + * Prior creating a domain (for instance via virDomainCreateXML + * or virDomainDefineXML) it may be suitable to know what the + * underlying emulator and/or libvirt is capable of. For + * instance, if host, libvirt and qemu is capable of VFIO + * passthrough and so on. + * + * Returns NULL in case of error, or an XML string
s/error,/error/ FWIW: Use of the comma in this construct "looks odd" as if there was some third option. Think of it this way "a or b" or "a, b, or c" where the comma is added in the second construct to separate list elements. Seems the rest is right to me - although I guess the answer to whether NULL is "expected" or not is something that needs to be answered before there's an ACK. John
+ * defining the capabilities. + */ +char * +virConnectGetDomainCapabilities(virConnectPtr conn, + const char *emulatorbin, + const char *arch, + const char *machine, + const char *virttype, + unsigned int flags) +{ + VIR_DEBUG("conn=%p, emulatorbin=%s, arch=%s, " + "machine=%s, virttype=%s, flags=%x", + conn, emulatorbin, arch, machine, virttype, flags); +
There's nothing that's stopping someone from passing NULL for emulatorbin, arch, machine, & virttype...
+ virResetLastError(); + + virCheckConnectReturn(conn, NULL); + + if (conn->driver->connectGetDomainCapabilities) { + char *ret; + ret = conn->driver->connectGetDomainCapabilities(conn, emulatorbin, + arch, machine, + virttype, flags); + if (!ret) + goto error; + VIR_DEBUG("conn=%p, ret=%s", conn, ret); + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(conn); + return NULL; +} diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 65a5b43..9f4016a 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -665,4 +665,9 @@ LIBVIRT_1.2.6 { virNetworkGetDHCPLeases; } LIBVIRT_1.2.5;
+LIBVIRT_1.2.7 { + global: + virConnectGetDomainCapabilities; +} LIBVIRT_1.2.6; + # .... define new API here using predicted next version number .... diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 3c10d5c..88fc977 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -8004,6 +8004,7 @@ static virDriver remote_driver = { .domainGetTime = remoteDomainGetTime, /* 1.2.5 */ .domainSetTime = remoteDomainSetTime, /* 1.2.5 */ .nodeGetFreePages = remoteNodeGetFreePages, /* 1.2.6 */ + .connectGetDomainCapabilities = remoteConnectGetDomainCapabilities, /* 1.2.7 */ };
static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index bff2c47..5c316fb 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -478,6 +478,18 @@ struct remote_connect_get_capabilities_ret { remote_nonnull_string capabilities; };
+struct remote_connect_get_domain_capabilities_args { + remote_string emulatorbin; + remote_string arch; + remote_string machine; + remote_string virttype; + unsigned int flags; +}; + +struct remote_connect_get_domain_capabilities_ret { + remote_nonnull_string capabilities; +}; + struct remote_node_get_cpu_stats_args { int cpuNum; int nparams; @@ -5402,6 +5414,11 @@ enum remote_procedure { * @generate: none * @acl: network:read */ - REMOTE_PROC_NETWORK_GET_DHCP_LEASES = 341 + REMOTE_PROC_NETWORK_GET_DHCP_LEASES = 341,
+ /** + * @generate: both + * @acl: connect:write + */ + REMOTE_PROC_CONNECT_GET_DOMAIN_CAPABILITIES = 342 }; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index a14e1fd..9bf09b8 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -148,6 +148,16 @@ struct remote_node_get_info_ret { struct remote_connect_get_capabilities_ret { remote_nonnull_string capabilities; }; +struct remote_connect_get_domain_capabilities_args { + remote_string emulatorbin; + remote_string arch; + remote_string machine; + remote_string virttype; + u_int flags; +}; +struct remote_connect_get_domain_capabilities_ret { + remote_nonnull_string capabilities; +}; struct remote_node_get_cpu_stats_args { int cpuNum; int nparams; @@ -2851,4 +2861,5 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_EVENT_BLOCK_JOB_2 = 339, REMOTE_PROC_NODE_GET_FREE_PAGES = 340, REMOTE_PROC_NETWORK_GET_DHCP_LEASES = 341, + REMOTE_PROC_CONNECT_GET_DOMAIN_CAPABILITIES = 342, };

The API is exposed under 'domcapabilities' command. Currently, with the variety of drivers that libvirt supports, none of the command arguments is obligatory, but all are optional instead. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- tools/virsh-host.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 16 +++++++++++ 2 files changed, 100 insertions(+) diff --git a/tools/virsh-host.c b/tools/virsh-host.c index 734f1a8..a1d8465 100644 --- a/tools/virsh-host.c +++ b/tools/virsh-host.c @@ -69,6 +69,84 @@ cmdCapabilities(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) } /* + * "domcapabilities" command + */ +static const vshCmdInfo info_domcapabilities[] = { + {.name = "help", + .data = N_("domain capabilities") + }, + {.name = "desc", + .data = N_("Returns capabilities of emulator with respect to host and libvirt.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_domcapabilities[] = { + {.name = "virttype", + .type = VSH_OT_STRING, + .help = N_("virtualization type (/domain/@type)"), + }, + {.name = "emulatorbin", + .type = VSH_OT_STRING, + .help = N_("path to emulator binary (/domain/devices/emulator)"), + }, + {.name = "arch", + .type = VSH_OT_STRING, + .help = N_("domain architecture (/domain/os/type/@arch)"), + }, + {.name = "machine", + .type = VSH_OT_STRING, + .help = N_("machine type (/domain/os/type/@machine)"), + }, + {.name = NULL} +}; + +static bool +cmdDomCapabilities(vshControl *ctl, const vshCmd *cmd) +{ + bool ret = false; + char *caps; + const char *virttype = NULL; + const char *emulatorbin = NULL; + const char *arch = NULL; + const char *machine = NULL; + const unsigned int flags = 0; /* No flags so far */ + + if (vshCommandOptString(cmd, "virttype", &virttype) < 0) { + vshError(ctl, "%s", _("ble")); + goto cleanup; + } + + if (vshCommandOptString(cmd, "emulatorbin", &emulatorbin) < 0) { + vshError(ctl, "%s", _("ble")); + goto cleanup; + } + + if (vshCommandOptString(cmd, "arch", &arch) < 0) { + vshError(ctl, "%s", _("ble")); + goto cleanup; + } + + if (vshCommandOptString(cmd, "machine", &machine) < 0) { + vshError(ctl, "%s", _("ble")); + goto cleanup; + } + + caps = virConnectGetDomainCapabilities(ctl->conn, emulatorbin, + arch, machine, virttype, flags); + if (!caps) { + vshError(ctl, "%s", _("failed to get emulator capabilities")); + goto cleanup; + } + + vshPrint(ctl, "%s\n", caps); + ret = true; + cleanup: + VIR_FREE(caps); + return ret; +} + +/* * "freecell" command */ static const vshCmdInfo info_freecell[] = { @@ -1131,6 +1209,12 @@ const vshCmdDef hostAndHypervisorCmds[] = { .info = info_cpu_models, .flags = 0 }, + {.name = "domcapabilities", + .handler = cmdDomCapabilities, + .opts = opts_domcapabilities, + .info = info_domcapabilities, + .flags = 0 + }, {.name = "freecell", .handler = cmdFreecell, .opts = opts_freecell, diff --git a/tools/virsh.pod b/tools/virsh.pod index b248c9a..b37a2be 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -350,6 +350,22 @@ description see: L<http://libvirt.org/formatcaps.html> The XML also show the NUMA topology information if available. +=item B<domcapabilities> [I<virttype>] [I<emulatorbin>] +[I<arch>] [I<machine>] + +Print an XML document describing the capabilities of the +hypervisor we are currently connected to. This may be useful if +you intend to create a new domain and are curious if for instance +should use VFIO or legacy KVM device passthrough. The I<virttype> +specifies the virtualization used (the domain XML counterpart is +the 'type' attribute of the <domain/> top level element). Then, +the I<emulatorbin> specifies the path to the emulator (this is +same as <emulator> element in the domain XML). Then, the I<arch> +argument sets the domain architecture (exposed under +/domain/os/type/@arch attribute). Then at last I<machine> +overrides the default machine for the emulator (can be found in +domain XML under /domain/os/type). + =item B<inject-nmi> I<domain> Inject NMI to the guest. -- 1.8.5.5

On Mon, Jun 30, 2014 at 05:31:46PM +0200, Michal Privoznik wrote:
The API is exposed under 'domcapabilities' command. Currently, with the variety of drivers that libvirt supports, none of the command arguments is obligatory, but all are optional instead.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- tools/virsh-host.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 16 +++++++++++ 2 files changed, 100 insertions(+)
ACK
+static bool +cmdDomCapabilities(vshControl *ctl, const vshCmd *cmd) +{ + bool ret = false; + char *caps; + const char *virttype = NULL; + const char *emulatorbin = NULL; + const char *arch = NULL; + const char *machine = NULL; + const unsigned int flags = 0; /* No flags so far */ + + if (vshCommandOptString(cmd, "virttype", &virttype) < 0) { + vshError(ctl, "%s", _("ble"));
I imagine "ble" was a placeholder you meant to replace :-)
+ goto cleanup; + } + + if (vshCommandOptString(cmd, "emulatorbin", &emulatorbin) < 0) { + vshError(ctl, "%s", _("ble")); + goto cleanup; + } + + if (vshCommandOptString(cmd, "arch", &arch) < 0) { + vshError(ctl, "%s", _("ble")); + goto cleanup; + } + + if (vshCommandOptString(cmd, "machine", &machine) < 0) { + vshError(ctl, "%s", _("ble")); + goto cleanup; + }
ACK if that is fixed. Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 06/30/2014 11:31 AM, Michal Privoznik wrote:
The API is exposed under 'domcapabilities' command. Currently, with the variety of drivers that libvirt supports, none of the command arguments is obligatory, but all are optional instead.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- tools/virsh-host.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 16 +++++++++++ 2 files changed, 100 insertions(+)
diff --git a/tools/virsh-host.c b/tools/virsh-host.c index 734f1a8..a1d8465 100644 --- a/tools/virsh-host.c +++ b/tools/virsh-host.c @@ -69,6 +69,84 @@ cmdCapabilities(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) }
/* + * "domcapabilities" command + */ +static const vshCmdInfo info_domcapabilities[] = { + {.name = "help", + .data = N_("domain capabilities") + }, + {.name = "desc", + .data = N_("Returns capabilities of emulator with respect to host and libvirt.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_domcapabilities[] = { + {.name = "virttype", + .type = VSH_OT_STRING, + .help = N_("virtualization type (/domain/@type)"), + }, + {.name = "emulatorbin", + .type = VSH_OT_STRING, + .help = N_("path to emulator binary (/domain/devices/emulator)"), + }, + {.name = "arch", + .type = VSH_OT_STRING, + .help = N_("domain architecture (/domain/os/type/@arch)"), + }, + {.name = "machine", + .type = VSH_OT_STRING, + .help = N_("machine type (/domain/os/type/@machine)"), + }, + {.name = NULL} +}; + +static bool +cmdDomCapabilities(vshControl *ctl, const vshCmd *cmd) +{ + bool ret = false; + char *caps; + const char *virttype = NULL; + const char *emulatorbin = NULL; + const char *arch = NULL; + const char *machine = NULL; + const unsigned int flags = 0; /* No flags so far */ + + if (vshCommandOptString(cmd, "virttype", &virttype) < 0) { + vshError(ctl, "%s", _("ble")); + goto cleanup; + } + + if (vshCommandOptString(cmd, "emulatorbin", &emulatorbin) < 0) { + vshError(ctl, "%s", _("ble")); + goto cleanup; + } + + if (vshCommandOptString(cmd, "arch", &arch) < 0) { + vshError(ctl, "%s", _("ble")); + goto cleanup; + } + + if (vshCommandOptString(cmd, "machine", &machine) < 0) { + vshError(ctl, "%s", _("ble")); + goto cleanup; + }
None of the above have realistic error messages. If they're not found on the command line, then how should things proceed? Although the code in the qemu driver seems to only care that virttype is provided. The qemu driver also requires arch or emulatorbin to be provided in order to fetch machine (and other caps).
+ + caps = virConnectGetDomainCapabilities(ctl->conn, emulatorbin, + arch, machine, virttype, flags); + if (!caps) { + vshError(ctl, "%s", _("failed to get emulator capabilities")); + goto cleanup; + } + + vshPrint(ctl, "%s\n", caps); + ret = true; + cleanup: + VIR_FREE(caps); + return ret; +} + +/* * "freecell" command */ static const vshCmdInfo info_freecell[] = { @@ -1131,6 +1209,12 @@ const vshCmdDef hostAndHypervisorCmds[] = { .info = info_cpu_models, .flags = 0 }, + {.name = "domcapabilities", + .handler = cmdDomCapabilities, + .opts = opts_domcapabilities, + .info = info_domcapabilities, + .flags = 0 + }, {.name = "freecell", .handler = cmdFreecell, .opts = opts_freecell, diff --git a/tools/virsh.pod b/tools/virsh.pod index b248c9a..b37a2be 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -350,6 +350,22 @@ description see: L<http://libvirt.org/formatcaps.html> The XML also show the NUMA topology information if available.
+=item B<domcapabilities> [I<virttype>] [I<emulatorbin>] +[I<arch>] [I<machine>] + +Print an XML document describing the capabilities of the +hypervisor we are currently connected to. This may be useful if +you intend to create a new domain and are curious if for instance +should use VFIO or legacy KVM device passthrough. The I<virttype> +specifies the virtualization used (the domain XML counterpart is +the 'type' attribute of the <domain/> top level element). Then, +the I<emulatorbin> specifies the path to the emulator (this is +same as <emulator> element in the domain XML). Then, the I<arch> +argument sets the domain architecture (exposed under +/domain/os/type/@arch attribute). Then at last I<machine> +overrides the default machine for the emulator (can be found in +domain XML under /domain/os/type). +
From just the description, I would think I could get all the caps for the domain of the hypervisor that virsh is connected to. Although I still have to provide the virttype (which should be part of the connection right)? I guess this goes along with my comments from patch 1 about not having a clear enough picture yet to know the best way to describe the virsh.pod fields.
Once that picture is clearer I figure it'll be easier for me at least to provide some text advice here... For starters though, the "Then, the" for each option is superfluous - just go with "The"... Also, "...and are curious if for instance should use VFIO..." doesn't read well or convey (at least to me) what to expect. John
=item B<inject-nmi> I<domain>
Inject NMI to the guest.

On 06/30/2014 11:31 AM, Michal Privoznik wrote:
The API is exposed under 'domcapabilities' command. Currently, with the variety of drivers that libvirt supports, none of the command arguments is obligatory, but all are optional instead.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- tools/virsh-host.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 16 +++++++++++ 2 files changed, 100 insertions(+)
diff --git a/tools/virsh-host.c b/tools/virsh-host.c index 734f1a8..a1d8465 100644 --- a/tools/virsh-host.c +++ b/tools/virsh-host.c @@ -69,6 +69,84 @@ cmdCapabilities(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) }
/* + * "domcapabilities" command + */ +static const vshCmdInfo info_domcapabilities[] = { + {.name = "help", + .data = N_("domain capabilities") + }, + {.name = "desc", + .data = N_("Returns capabilities of emulator with respect to host and libvirt.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_domcapabilities[] = { + {.name = "virttype", + .type = VSH_OT_STRING, + .help = N_("virtualization type (/domain/@type)"), + }, + {.name = "emulatorbin", + .type = VSH_OT_STRING, + .help = N_("path to emulator binary (/domain/devices/emulator)"), + }, + {.name = "arch", + .type = VSH_OT_STRING, + .help = N_("domain architecture (/domain/os/type/@arch)"), + }, + {.name = "machine", + .type = VSH_OT_STRING, + .help = N_("machine type (/domain/os/type/@machine)"), + }, + {.name = NULL} +}; + +static bool +cmdDomCapabilities(vshControl *ctl, const vshCmd *cmd) +{ + bool ret = false; + char *caps; + const char *virttype = NULL; + const char *emulatorbin = NULL; + const char *arch = NULL; + const char *machine = NULL; + const unsigned int flags = 0; /* No flags so far */ + + if (vshCommandOptString(cmd, "virttype", &virttype) < 0) { + vshError(ctl, "%s", _("ble")); + goto cleanup; + } + + if (vshCommandOptString(cmd, "emulatorbin", &emulatorbin) < 0) { + vshError(ctl, "%s", _("ble")); + goto cleanup; + } + + if (vshCommandOptString(cmd, "arch", &arch) < 0) { + vshError(ctl, "%s", _("ble")); + goto cleanup; + } + + if (vshCommandOptString(cmd, "machine", &machine) < 0) { + vshError(ctl, "%s", _("ble")); + goto cleanup; + } + + caps = virConnectGetDomainCapabilities(ctl->conn, emulatorbin, + arch, machine, virttype, flags); + if (!caps) { + vshError(ctl, "%s", _("failed to get emulator capabilities")); + goto cleanup; + } + + vshPrint(ctl, "%s\n", caps); + ret = true; + cleanup: + VIR_FREE(caps); + return ret; +} + +/* * "freecell" command */ static const vshCmdInfo info_freecell[] = { @@ -1131,6 +1209,12 @@ const vshCmdDef hostAndHypervisorCmds[] = { .info = info_cpu_models, .flags = 0 }, + {.name = "domcapabilities", + .handler = cmdDomCapabilities, + .opts = opts_domcapabilities, + .info = info_domcapabilities, + .flags = 0 + }, {.name = "freecell", .handler = cmdFreecell, .opts = opts_freecell, diff --git a/tools/virsh.pod b/tools/virsh.pod index b248c9a..b37a2be 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -350,6 +350,22 @@ description see: L<http://libvirt.org/formatcaps.html> The XML also show the NUMA topology information if available.
Oy - ...the XML also show*s* the NUMA...
+=item B<domcapabilities> [I<virttype>] [I<emulatorbin>] +[I<arch>] [I<machine>] + +Print an XML document describing the capabilities of the +hypervisor we are currently connected to. This may be useful if +you intend to create a new domain and are curious if for instance +should use VFIO or legacy KVM device passthrough. The I<virttype> +specifies the virtualization used (the domain XML counterpart is +the 'type' attribute of the <domain/> top level element). Then, +the I<emulatorbin> specifies the path to the emulator (this is +same as <emulator> element in the domain XML). Then, the I<arch> +argument sets the domain architecture (exposed under +/domain/os/type/@arch attribute). Then at last I<machine> +overrides the default machine for the emulator (can be found in +domain XML under /domain/os/type). +
[OK so given everything thus far...] Print an XML document describing the domain capabilities for the hypervisor we are connected to using information either sourced from an existing domain or taken from the B<virsh capabilities> output. This may be useful if you intend to create a new domain and are curious if for instance it could make use of VFIO by creating a domain for the hypervisor with a specific emulator and architecture. Each hypervisor will have different requirements regarding which options are required and which are optional. A hypervisor can support providing a default value for any of the options. The I<virttype> option specifies the virtualization type used. The value to be used is either from the 'type' attribute of the <domain/> top level element from the domain XML or the 'type' attribute found within each <guest/> element from the B<virsh capabilities> output. The I<emulatorbin> option specifies the path to the emulator. The value to be used is either the <emulator> element in the domain XML or the B<virsh capabilities> output. The I<arch> option specifies the architecture to be used for the domain. The value to be used is either the "arch" attribute from the domain's XML <os/> element and <type/> subelement or the "name" attribute of an <arch/> element from the B<virsh capabililites> output. The I<machine> specifies the machine type for the emulator. The value to be used is either the "machine" attribute from the domain's XML <os/> element and <type/> subelement or one from a list of machines from the B<virsh capabilities> output for a specific architecture and domain type. For the qemu hypervisor, a I<virttype> of either 'qemu' or 'kvm' must be supplied along with either the I<emulatorbin> or I<arch> in order to generate output for the default I<machine>. Supplying a I<machine> value will generate output for the specific machine. [Hope I got this right...]
=item B<inject-nmi> I<domain>
Inject NMI to the guest.

Later on, we the qemu capabilities XML parsing code may come handy so instead of duplicating the code make the already existing one shared. By the same time, make the function accept file name instead of XML document stored already in memory. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- tests/qemucapabilitiestest.c | 57 +++----------------------------------------- tests/testutilsqemu.c | 49 +++++++++++++++++++++++++++++++++++++ tests/testutilsqemu.h | 3 +++ 3 files changed, 55 insertions(+), 54 deletions(-) diff --git a/tests/qemucapabilitiestest.c b/tests/qemucapabilitiestest.c index a291460..4e5f9e5 100644 --- a/tests/qemucapabilitiestest.c +++ b/tests/qemucapabilitiestest.c @@ -85,55 +85,6 @@ testQemuFeedMonitor(char *replies, return NULL; } -static virQEMUCapsPtr -testQemuGetCaps(char *caps) -{ - virQEMUCapsPtr qemuCaps = NULL; - xmlDocPtr xml; - xmlXPathContextPtr ctxt = NULL; - ssize_t i, n; - xmlNodePtr *nodes = NULL; - - if (!(xml = virXMLParseStringCtxt(caps, "(test caps)", &ctxt))) - goto error; - - if ((n = virXPathNodeSet("/qemuCaps/flag", ctxt, &nodes)) < 0) { - fprintf(stderr, "failed to parse qemu capabilities flags"); - goto error; - } - - if (n > 0) { - if (!(qemuCaps = virQEMUCapsNew())) - goto error; - - for (i = 0; i < n; i++) { - char *str = virXMLPropString(nodes[i], "name"); - if (str) { - int flag = virQEMUCapsTypeFromString(str); - if (flag < 0) { - fprintf(stderr, "Unknown qemu capabilities flag %s", str); - VIR_FREE(str); - goto error; - } - VIR_FREE(str); - virQEMUCapsSet(qemuCaps, flag); - } - } - } - - VIR_FREE(nodes); - xmlFreeDoc(xml); - xmlXPathFreeContext(ctxt); - return qemuCaps; - - error: - VIR_FREE(nodes); - virObjectUnref(qemuCaps); - xmlFreeDoc(xml); - xmlXPathFreeContext(ctxt); - return NULL; -} - static int testQemuCapsCompare(virQEMUCapsPtr capsProvided, virQEMUCapsPtr capsComputed) @@ -166,7 +117,7 @@ testQemuCaps(const void *opaque) int ret = -1; const testQemuData *data = opaque; char *repliesFile = NULL, *capsFile = NULL; - char *replies = NULL, *caps = NULL; + char *replies = NULL; qemuMonitorTestPtr mon = NULL; virQEMUCapsPtr capsProvided = NULL, capsComputed = NULL; @@ -176,14 +127,13 @@ testQemuCaps(const void *opaque) abs_srcdir, data->base) < 0) goto cleanup; - if (virtTestLoadFile(repliesFile, &replies) < 0 || - virtTestLoadFile(capsFile, &caps) < 0) + if (virtTestLoadFile(repliesFile, &replies) < 0) goto cleanup; if (!(mon = testQemuFeedMonitor(replies, data->xmlopt))) goto cleanup; - if (!(capsProvided = testQemuGetCaps(caps))) + if (!(capsProvided = qemuTestParseCapabilities(capsFile))) goto cleanup; if (!(capsComputed = virQEMUCapsNew())) @@ -207,7 +157,6 @@ testQemuCaps(const void *opaque) VIR_FREE(repliesFile); VIR_FREE(capsFile); VIR_FREE(replies); - VIR_FREE(caps); qemuMonitorTestFree(mon); virObjectUnref(capsProvided); virObjectUnref(capsComputed); diff --git a/tests/testutilsqemu.c b/tests/testutilsqemu.c index 7e24909..85f0356 100644 --- a/tests/testutilsqemu.c +++ b/tests/testutilsqemu.c @@ -373,4 +373,53 @@ testSCSIDeviceGetSgName(const char *sysfs_prefix ATTRIBUTE_UNUSED, qemuBuildCommandLineCallbacks testCallbacks = { .qemuGetSCSIDeviceSgName = testSCSIDeviceGetSgName, }; + +virQEMUCapsPtr +qemuTestParseCapabilities(const char *capsFile) +{ + virQEMUCapsPtr qemuCaps = NULL; + xmlDocPtr xml; + xmlXPathContextPtr ctxt = NULL; + ssize_t i, n; + xmlNodePtr *nodes = NULL; + + if (!(xml = virXMLParseFileCtxt(capsFile, &ctxt))) + goto error; + + if ((n = virXPathNodeSet("/qemuCaps/flag", ctxt, &nodes)) < 0) { + fprintf(stderr, "failed to parse qemu capabilities flags"); + goto error; + } + + if (n > 0) { + if (!(qemuCaps = virQEMUCapsNew())) + goto error; + + for (i = 0; i < n; i++) { + char *str = virXMLPropString(nodes[i], "name"); + if (str) { + int flag = virQEMUCapsTypeFromString(str); + if (flag < 0) { + fprintf(stderr, "Unknown qemu capabilities flag %s", str); + VIR_FREE(str); + goto error; + } + VIR_FREE(str); + virQEMUCapsSet(qemuCaps, flag); + } + } + } + + VIR_FREE(nodes); + xmlFreeDoc(xml); + xmlXPathFreeContext(ctxt); + return qemuCaps; + + error: + VIR_FREE(nodes); + virObjectUnref(qemuCaps); + xmlFreeDoc(xml); + xmlXPathFreeContext(ctxt); + return NULL; +} #endif diff --git a/tests/testutilsqemu.h b/tests/testutilsqemu.h index f01b722..79ee143 100644 --- a/tests/testutilsqemu.h +++ b/tests/testutilsqemu.h @@ -3,8 +3,11 @@ # include "capabilities.h" # include "domain_conf.h" # include "qemu/qemu_command.h" +# include "qemu/qemu_capabilities.h" virCapsPtr testQemuCapsInit(void); virDomainXMLOptionPtr testQemuXMLConfInit(void); extern qemuBuildCommandLineCallbacks testCallbacks; + +virQEMUCapsPtr qemuTestParseCapabilities(const char *capsFile); #endif -- 1.8.5.5

On Mon, Jun 30, 2014 at 05:31:47PM +0200, Michal Privoznik wrote:
Later on, we the qemu capabilities XML parsing code may come handy so instead of duplicating the code make the already existing one shared. By the same time, make the function accept file name instead of XML document stored already in memory.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- tests/qemucapabilitiestest.c | 57 +++----------------------------------------- tests/testutilsqemu.c | 49 +++++++++++++++++++++++++++++++++++++ tests/testutilsqemu.h | 3 +++ 3 files changed, 55 insertions(+), 54 deletions(-)
ACK Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

The API may come handy if somebody has an architecture and wants to look through available qemus if the architecture is supported or not. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_capabilities.c | 33 +++++++++++++++++++++++++++++++++ src/qemu/qemu_capabilities.h | 2 ++ 2 files changed, 35 insertions(+) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 8e0a550..b357255 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -299,6 +299,10 @@ struct _virQEMUCapsCache { gid_t runGid; }; +struct virQEMUCapsSearchData { + virArch arch; +}; + static virClassPtr virQEMUCapsClass; static void virQEMUCapsDispose(void *obj); @@ -3465,6 +3469,35 @@ virQEMUCapsCacheLookupCopy(virQEMUCapsCachePtr cache, const char *binary) } +static int +virQEMUCapsCompareArch(const void *payload, + const void *name ATTRIBUTE_UNUSED, + const void *opaque) +{ + struct virQEMUCapsSearchData *data = (struct virQEMUCapsSearchData *) opaque; + const virQEMUCaps *qemuCaps = payload; + + return qemuCaps->arch == data->arch; +} + + +virQEMUCapsPtr +virQEMUCapsCacheLookupByArch(virQEMUCapsCachePtr cache, + virArch arch) +{ + virQEMUCapsPtr ret = NULL; + struct virQEMUCapsSearchData data = { .arch = arch }; + + virMutexLock(&cache->lock); + ret = virHashSearch(cache->binaries, virQEMUCapsCompareArch, &data); + VIR_DEBUG("Returning caps %p for arch %s", ret, virArchToString(arch)); + virObjectRef(ret); + virMutexUnlock(&cache->lock); + + return ret; +} + + void virQEMUCapsCacheFree(virQEMUCapsCachePtr cache) { diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index 53ebe90..1eb92b5 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -274,6 +274,8 @@ virQEMUCapsPtr virQEMUCapsCacheLookup(virQEMUCapsCachePtr cache, const char *binary); virQEMUCapsPtr virQEMUCapsCacheLookupCopy(virQEMUCapsCachePtr cache, const char *binary); +virQEMUCapsPtr virQEMUCapsCacheLookupByArch(virQEMUCapsCachePtr cache, + virArch arch); void virQEMUCapsCacheFree(virQEMUCapsCachePtr cache); virCapsPtr virQEMUCapsInit(virQEMUCapsCachePtr cache); -- 1.8.5.5

On Mon, Jun 30, 2014 at 05:31:48PM +0200, Michal Privoznik wrote:
The API may come handy if somebody has an architecture and wants to look through available qemus if the architecture is supported or not.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_capabilities.c | 33 +++++++++++++++++++++++++++++++++ src/qemu/qemu_capabilities.h | 2 ++ 2 files changed, 35 insertions(+)
ACK Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

This internal API is meant to answer the question 'Is this machine type supported by given qemu?'. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_capabilities.c | 14 ++++++++++++++ src/qemu/qemu_capabilities.h | 3 +++ 2 files changed, 17 insertions(+) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index b357255..46e469c 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -3542,3 +3542,17 @@ virQEMUCapsSupportsChardev(virDomainDefPtr def, (chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE && chr->targetType == VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_VIRTIO)); } + + +bool +virQEMUCapsIsMachineSupported(virQEMUCapsPtr qemuCaps, + const char *canonical_machine) +{ + size_t i; + + for (i = 0; i < qemuCaps->nmachineTypes; i++) { + if (STREQ(canonical_machine, qemuCaps->machineTypes[i])) + return true; + } + return false; +} diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index 1eb92b5..4857dfd 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -302,6 +302,9 @@ bool virQEMUCapsSupportsChardev(virDomainDefPtr def, virQEMUCapsPtr qemuCaps, virDomainChrDefPtr chr); +bool virQEMUCapsIsMachineSupported(virQEMUCapsPtr qemuCaps, + const char *canonical_machine); + int virQEMUCapsInitGuestFromBinary(virCapsPtr caps, const char *binary, virQEMUCapsPtr qemubinCaps, -- 1.8.5.5

On Mon, Jun 30, 2014 at 05:31:49PM +0200, Michal Privoznik wrote:
This internal API is meant to answer the question 'Is this machine type supported by given qemu?'.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_capabilities.c | 14 ++++++++++++++ src/qemu/qemu_capabilities.h | 3 +++ 2 files changed, 17 insertions(+)
ACK Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

Sometimes it may be useful to get a default machine for given qemu binary. Fortunately, the default machine is stored always on the first position in the supported machines array. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_capabilities.c | 9 +++++++++ src/qemu/qemu_capabilities.h | 2 ++ 2 files changed, 11 insertions(+) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 46e469c..e4336e1 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -3556,3 +3556,12 @@ virQEMUCapsIsMachineSupported(virQEMUCapsPtr qemuCaps, } return false; } + + +const char * +virQEMUCapsGetDefaultMachine(virQEMUCapsPtr qemuCaps) +{ + if (!qemuCaps->nmachineTypes) + return NULL; + return qemuCaps->machineTypes[0]; +} diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index 4857dfd..d0a1092 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -305,6 +305,8 @@ bool virQEMUCapsSupportsChardev(virDomainDefPtr def, bool virQEMUCapsIsMachineSupported(virQEMUCapsPtr qemuCaps, const char *canonical_machine); +const char * virQEMUCapsGetDefaultMachine(virQEMUCapsPtr qemuCaps); + int virQEMUCapsInitGuestFromBinary(virCapsPtr caps, const char *binary, virQEMUCapsPtr qemubinCaps, -- 1.8.5.5

On Mon, Jun 30, 2014 at 05:31:50PM +0200, Michal Privoznik wrote:
Sometimes it may be useful to get a default machine for given qemu binary. Fortunately, the default machine is stored always on the first position in the supported machines array.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_capabilities.c | 9 +++++++++ src/qemu/qemu_capabilities.h | 2 ++ 2 files changed, 11 insertions(+)
ACK Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

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

On Mon, Jun 30, 2014 at 05:31:51PM +0200, Michal Privoznik wrote:
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 | 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
+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);
I have a feeling that 'SD' is not supported in all QEMU's we claim to work with, though perhaps we've never checked this before when building the CLI args.
+ + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_USB_STORAGE)) + VIR_DOMAIN_CAPS_ENUM_SET(disk->bus, VIR_DOMAIN_DISK_BUS_USB); +} + +
+ <hostdev supported='yes'> + <enum name='mode'/>
Hmm, so that's claiming we don't support any values for the mode attribute, but we support subsys.
+ <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'/>
Yep, only LXC supports the caps mode.
+ <enum name='pciBackend'> + <value>default</value> + <value>kvm</value> + <value>vfio</value> + </enum> + </hostdev> + </devices> +</domainCapabilities>
Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 02.07.2014 16:56, Daniel P. Berrange wrote:
On Mon, Jun 30, 2014 at 05:31:51PM +0200, Michal Privoznik wrote:
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 | 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
+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);
I have a feeling that 'SD' is not supported in all QEMU's we claim to work with, though perhaps we've never checked this before when building the CLI args.
Well, we don't. I haven't found any code that checks for 'SD' in qemu driver. I can remove it until the time we have a resolution.
+ + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_USB_STORAGE)) + VIR_DOMAIN_CAPS_ENUM_SET(disk->bus, VIR_DOMAIN_DISK_BUS_USB); +} + +
+ <hostdev supported='yes'> + <enum name='mode'/>
Hmm, so that's claiming we don't support any values for the mode attribute, but we support subsys.
Ouch, yeah. The problem is, I was not setting the correct struct member. Here's the fix: index 9fc58ff..6ed85a9 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -3603,7 +3603,7 @@ virQEMUCapsFillDomainDeviceHostdevCaps(virQEMUCapsPtr qemuCaps, hostdev->device.supported = true; /* VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES is for containers only */ - VIR_DOMAIN_CAPS_ENUM_SET(hostdev->subsysType, + VIR_DOMAIN_CAPS_ENUM_SET(hostdev->mode, VIR_DOMAIN_HOSTDEV_MODE_SUBSYS); VIR_DOMAIN_CAPS_ENUM_SET(hostdev->startupPolicy, diff --git a/tests/domaincapsschemadata/domaincaps-qemu_1.6.50-1.xml b/tests/domaincapsschemadata/domaincaps-qemu_1.6.50-1.xml index 562e2f4..b7d9c26 100644 --- a/tests/domaincapsschemadata/domaincaps-qemu_1.6.50-1.xml +++ b/tests/domaincapsschemadata/domaincaps-qemu_1.6.50-1.xml @@ -21,7 +21,9 @@ </enum> </disk> <hostdev supported='yes'> - <enum name='mode'/> + <enum name='mode'> + <value>subsystem</value> + </enum> <enum name='startupPolicy'> <value>default</value> <value>mandatory</value> Michal

On Thu, Jul 03, 2014 at 11:55:44AM +0200, Michal Privoznik wrote:
On 02.07.2014 16:56, Daniel P. Berrange wrote:
On Mon, Jun 30, 2014 at 05:31:51PM +0200, Michal Privoznik wrote:
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 | 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
+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);
I have a feeling that 'SD' is not supported in all QEMU's we claim to work with, though perhaps we've never checked this before when building the CLI args.
Well, we don't. I haven't found any code that checks for 'SD' in qemu driver. I can remove it until the time we have a resolution.
+ + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_USB_STORAGE)) + VIR_DOMAIN_CAPS_ENUM_SET(disk->bus, VIR_DOMAIN_DISK_BUS_USB); +} + +
+ <hostdev supported='yes'> + <enum name='mode'/>
Hmm, so that's claiming we don't support any values for the mode attribute, but we support subsys.
Ouch, yeah. The problem is, I was not setting the correct struct member. Here's the fix:
index 9fc58ff..6ed85a9 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -3603,7 +3603,7 @@ virQEMUCapsFillDomainDeviceHostdevCaps(virQEMUCapsPtr qemuCaps,
hostdev->device.supported = true; /* VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES is for containers only */ - VIR_DOMAIN_CAPS_ENUM_SET(hostdev->subsysType, + VIR_DOMAIN_CAPS_ENUM_SET(hostdev->mode, VIR_DOMAIN_HOSTDEV_MODE_SUBSYS);
VIR_DOMAIN_CAPS_ENUM_SET(hostdev->startupPolicy, diff --git a/tests/domaincapsschemadata/domaincaps-qemu_1.6.50-1.xml b/tests/domaincapsschemadata/domaincaps-qemu_1.6.50-1.xml index 562e2f4..b7d9c26 100644 --- a/tests/domaincapsschemadata/domaincaps-qemu_1.6.50-1.xml +++ b/tests/domaincapsschemadata/domaincaps-qemu_1.6.50-1.xml @@ -21,7 +21,9 @@ </enum> </disk> <hostdev supported='yes'> - <enum name='mode'/> + <enum name='mode'> + <value>subsystem</value> + </enum> <enum name='startupPolicy'> <value>default</value> <value>mandatory</value>
ACK Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
participants (3)
-
Daniel P. Berrange
-
John Ferlan
-
Michal Privoznik