[libvirt] [PATCH for 1.2.7 1/8] Introduce domain_capabilities

John Ferlan jferlan at redhat.com
Wed Jul 2 14:32:59 UTC 2014



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




More information about the libvir-list mailing list