Parse <emulator> elements from virConnectGetCapabilities()

Extend the associated LibvirtConfigCapsGuest class so that it can
parse and store <emulator> elements in a way which can be reused in
subsequent commits by callers of virConnectGetDomainCapabilities().

Ongoing / future work such as SEV[0] and the move of the default machine
type to q35[1] will need to invoke virConnectGetDomainCapabilities()
in order to check whether SEV or q35 respectively are supported.
However this API requires 5 parameters[2]:

   1) the path to the emulator binary (e.g. qemu-system-x86_64)
   2) the domain architecture
   3) the machine type
   4) the virtualization type
   5) flags (not used yet)

4) is determined by CONF.libvirt.virt_type.  The caller of
virConnectGetDomainCapabilities() can decide which combinations of 2)
and 3) type to pass[3], but still needs to know 1).  Once 2) and 3)
are known, this can be determined from the <emulator> elements
returned by libvirt's virConnectGetCapabilities() API[4].  nova
already calls this and parses the response into a LibvirtConfigCaps
object.  However currently the parser ignores the <emulator> elements.
This patch fixes that.

[0] https://specs.openstack.org/openstack/nova-specs/specs/stein/approved/amd-sev-libvirt-support.html
[1] https://blueprints.launchpad.net/nova/+spec/handle-default-machine-type-as-q35
[2] https://libvirt.org/html/libvirt-libvirt-domain.html#virConnectGetDomainCapabilities
[3] Calling virConnectGetDomainCapabilities for every machine type is
    most likely overkill:
    https://bugzilla.redhat.com/show_bug.cgi?id=1683471#c7
[4] https://libvirt.org/formatcaps.html#elementGuest

blueprint: amd-sev-libvirt-support
blueprint: handle-default-machine-type-as-q35
Change-Id: Ibdc88bc5e0a214bff36a8351a1b76980f7015d16
This commit is contained in:
Adam Spiers 2019-02-27 17:36:59 +00:00
parent ffd81eb107
commit 48d6753d37
2 changed files with 53 additions and 5 deletions

View File

@ -115,11 +115,19 @@ class LibvirtConfigCapsTest(LibvirtConfigBaseTest):
</host>
<guest>
<os_type>hvm</os_type>
<arch name='x86_64'/>
<arch name='x86_64'>
<emulator>/usr/bin/qemu-system-x86_64</emulator>
<domain type="qemu" />
<domain type="kvm">
<emulator>/usr/bin/qemu-kvm</emulator>
</domain>
</arch>
</guest>
<guest>
<os_type>hvm</os_type>
<arch name='i686'/>
<arch name='i686'>
<emulator>/usr/bin/qemu-system-i386</emulator>
</arch>
</guest>
</capabilities>"""
@ -128,6 +136,17 @@ class LibvirtConfigCapsTest(LibvirtConfigBaseTest):
self.assertIsInstance(obj.host, config.LibvirtConfigCapsHost)
self.assertEqual(obj.host.uuid, "c7a5fdbd-edaf-9455-926a-d65c16db1809")
self.assertEqual(2, len(obj.guests))
for guest in obj.guests:
self.assertIsInstance(guest, config.LibvirtConfigCapsGuest)
self.assertEqual('hvm', guest.ostype)
self.assertEqual('x86_64', obj.guests[0].arch)
self.assertEqual('i686', obj.guests[1].arch)
self.assertEqual('/usr/bin/qemu-system-x86_64', obj.guests[0].emulator)
self.assertNotIn('qemu', obj.guests[0].domemulator)
self.assertEqual('/usr/bin/qemu-kvm', obj.guests[0].domemulator['kvm'])
self.assertEqual('/usr/bin/qemu-system-i386', obj.guests[1].emulator)
xmlout = obj.to_xml()

View File

@ -304,6 +304,21 @@ class LibvirtConfigCapsGuest(LibvirtConfigObject):
self.ostype = None
self.domtype = list()
# Track <emulator> values, which we need in order to be able
# to call virConnectGetDomainCapabilities() - typically
# something like '/usr/bin/qemu-system-i386'.
#
# Firstly we track the default for any <domain> child without
# its own <emulator> sub-child:
self.emulator = None
#
# Also per-<domain> overrides for the default in self.emulator.
# The dict maps domain types such as 'kvm' to the emulator
# path for that domain type. Note that these overrides come
# from <emulator> elements under each <domain>; there is no
# <domemulator> element.
self.domemulator = dict()
def parse_dom(self, xmldoc):
super(LibvirtConfigCapsGuest, self).parse_dom(xmldoc)
@ -312,9 +327,18 @@ class LibvirtConfigCapsGuest(LibvirtConfigObject):
self.ostype = c.text
elif c.tag == "arch":
self.arch = c.get("name")
for sc in c.getchildren():
if sc.tag == "domain":
self.domtype.append(sc.get("type"))
for ac in c.getchildren():
if ac.tag == "domain":
self.parse_domain(ac)
elif ac.tag == "emulator":
self.emulator = ac.text
def parse_domain(self, domxml):
domtype = domxml.get("type")
self.domtype.append(domtype)
for dc in domxml.getchildren():
if dc.tag == "emulator":
self.domemulator[domtype] = dc.text
def format_dom(self):
caps = super(LibvirtConfigCapsGuest, self).format_dom()
@ -323,9 +347,14 @@ class LibvirtConfigCapsGuest(LibvirtConfigObject):
caps.append(self._text_node("os_type", self.ostype))
if self.arch:
arch = etree.Element("arch", name=self.arch)
if self.emulator is not None:
arch.append(self._text_node("emulator", self.emulator))
for dt in self.domtype:
dte = etree.Element("domain")
dte.set("type", dt)
if dt in self.domemulator:
dte.append(self._text_node("emulator",
self.domemulator[dt]))
arch.append(dte)
caps.append(arch)