Track libvirt host/domain capabilities for multiple machine types
Currently we're only calling libvirt's getDomainCapabilities API once per architecture, with the assumption that covering a single machine type (the default) for that architecture is enough. However, the default for x86_64 is 'pc', but we need domain capabilities for 'q35' in order to allow guests with SEV or Secure Boot enabled. So for x86_64, we need domain capabilities for at least two machine types: 'pc' and 'q35'. We can obtain a sensibly small list of machine types with which to call getDomainCapabilities by looking for the 'canonical' attribute in machine types returned from getCapabilities (N.B. not getDomainCapabilities). For example, getCapabilities returns these: <machine canonical='pc-i440fx-2.11' maxCpus='255'>pc</machine> <machine canonical='pc-q35-2.11' maxCpus='288'>q35</machine> So change Host.get_domain_capabilities() to call the API not just with the default machine type, but also once per canonical machine type. In order to obtain the canonical machine types, enhance config.py so that it can extract them from the capabilities XML, modernize the fixtures for i686 and x86_64 capabilities so that they include recent versions of the 'pc' and 'q35' families of machine types, and add corresponding tests. A new class LibvirtConfigCapsGuestDomain is introduced to model the elements of the capabilities XML such as <domain type='kvm'>, and their associated machine types. This supersedes the previous domtype attribute of LibvirtConfigCapsGuest objects. Canonical machine types are tracked separately from the others in order to allow Host.get_domain_capabilities to invoke libvirt's getDomainCapabilities only on those types. As before, we register both the shortened canonical (alias) machine type (e.g. 'q35') plus its full expanded counterpart (e.g. 'pc-q35-2.11') if that is different. As Host.get_domain_capabilities() is already long and complex, and needs more functionality adding to support these changes, split out much of the code into smaller methods: - _get_machine_types() - _add_to_domain_capabilities() The new tests require the allow_mixed_nodes option of assertXmlEqual to be enabled to compare XML fragments with elements in different orders, since the children of <arch> generated by LibvirtConfigCapsGuest.format_dom have the non-canonical machine types first, followed by the canonical ones. The tests also require removing the 'bamboo' canonical machine type for the ppc architecture, since the getDomainCapabilities fixture for this architecture is static and only returns results with a fixed 'g3beige' machine type which would cause a mismatch when called with the 'bamboo' machine type. The same applies with the 'q35' canonical machine type for the i686 architecture, since the static fixture for i686 only returns results with a fixed 'pc-i440fx-2.11' machine type. However, the x86_64 fixtures are sufficient to test these code paths, so this is a simpler alternative to adding complexity to the i686 and ppc fixtures. blueprint: amd-sev-libvirt-support blueprint: allow-secure-boot-for-qemu-kvm-guests Change-Id: I9da9ce682dc8c8b72fb31dd4e732b556b2ed7f90
This commit is contained in:
parent
415ed543dd
commit
a53c867913
@ -48,40 +48,120 @@ CAPABILITIES_HOST_TEMPLATE = '''
|
||||
</secmodel>
|
||||
</host>'''
|
||||
|
||||
# NOTE(aspiers): HostTestCase has tests which assert that for any
|
||||
# given (arch, domain) listed in the guest capabilities here, all
|
||||
# canonical machine types (e.g. 'pc' or 'q35') must be a substring of
|
||||
# the expanded machine type returned in the <machine> element of the
|
||||
# corresponding fake getDomainCapabilities response for that (arch,
|
||||
# domain, canonical_machine_type) combination. Those responses are
|
||||
# defined by the DOMCAPABILITIES_* variables below. While
|
||||
# DOMCAPABILITIES_X86_64_TEMPLATE can return multiple values for the
|
||||
# <machine> element, DOMCAPABILITIES_I686 is fixed to fake a response
|
||||
# of the 'pc-i440fx-2.11' machine type, therefore
|
||||
# CAPABILITIES_GUEST['i686'] should return 'pc' as the only canonical
|
||||
# machine type.
|
||||
#
|
||||
# CAPABILITIES_GUEST does not include canonical machine types for
|
||||
# other non-x86 architectures, so these test assertions on apply to
|
||||
# x86.
|
||||
CAPABILITIES_GUEST = {
|
||||
'i686': '''
|
||||
<guest>
|
||||
<os_type>hvm</os_type>
|
||||
<arch name='i686'>
|
||||
<wordsize>32</wordsize>
|
||||
<emulator>/usr/bin/qemu</emulator>
|
||||
<machine>pc-0.14</machine>
|
||||
<machine canonical='pc-0.14'>pc</machine>
|
||||
<machine>pc-0.13</machine>
|
||||
<machine>pc-0.12</machine>
|
||||
<machine>pc-0.11</machine>
|
||||
<machine>pc-0.10</machine>
|
||||
<machine>isapc</machine>
|
||||
<domain type='qemu'>
|
||||
</domain>
|
||||
<emulator>/usr/bin/qemu-system-i386</emulator>
|
||||
<machine maxCpus='255'>pc-i440fx-2.11</machine>
|
||||
<machine canonical='pc-i440fx-2.11' maxCpus='255'>pc</machine>
|
||||
<machine maxCpus='1'>isapc</machine>
|
||||
<machine maxCpus='255'>pc-1.1</machine>
|
||||
<machine maxCpus='255'>pc-1.2</machine>
|
||||
<machine maxCpus='255'>pc-1.3</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.8</machine>
|
||||
<machine maxCpus='255'>pc-1.0</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.9</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.6</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.7</machine>
|
||||
<machine maxCpus='128'>xenfv</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.3</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.4</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.5</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.1</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.2</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.0</machine>
|
||||
<machine maxCpus='288'>pc-q35-2.11</machine>
|
||||
<machine maxCpus='288'>q35</machine>
|
||||
<machine maxCpus='1'>xenpv</machine>
|
||||
<machine maxCpus='288'>pc-q35-2.10</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-1.7</machine>
|
||||
<machine maxCpus='288'>pc-q35-2.9</machine>
|
||||
<machine maxCpus='255'>pc-0.15</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-1.5</machine>
|
||||
<machine maxCpus='255'>pc-q35-2.7</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-1.6</machine>
|
||||
<machine maxCpus='288'>pc-q35-2.8</machine>
|
||||
<machine maxCpus='255'>pc-0.13</machine>
|
||||
<machine maxCpus='255'>pc-0.14</machine>
|
||||
<machine maxCpus='255'>pc-q35-2.4</machine>
|
||||
<machine maxCpus='255'>pc-q35-2.5</machine>
|
||||
<machine maxCpus='255'>pc-q35-2.6</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-1.4</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.10</machine>
|
||||
<machine maxCpus='255'>pc-0.11</machine>
|
||||
<machine maxCpus='255'>pc-0.12</machine>
|
||||
<machine maxCpus='255'>pc-0.10</machine>
|
||||
<domain type='qemu'/>
|
||||
<domain type='kvm'>
|
||||
<emulator>/usr/bin/kvm</emulator>
|
||||
<machine>pc-0.14</machine>
|
||||
<machine canonical='pc-0.14'>pc</machine>
|
||||
<machine>pc-0.13</machine>
|
||||
<machine>pc-0.12</machine>
|
||||
<machine>pc-0.11</machine>
|
||||
<machine>pc-0.10</machine>
|
||||
<machine>isapc</machine>
|
||||
<emulator>/usr/bin/qemu-kvm</emulator>
|
||||
<machine maxCpus='255'>pc-i440fx-2.11</machine>
|
||||
<machine canonical='pc-i440fx-2.11' maxCpus='255'>pc</machine>
|
||||
<machine maxCpus='1'>isapc</machine>
|
||||
<machine maxCpus='255'>pc-1.1</machine>
|
||||
<machine maxCpus='255'>pc-1.2</machine>
|
||||
<machine maxCpus='255'>pc-1.3</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.8</machine>
|
||||
<machine maxCpus='255'>pc-1.0</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.9</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.6</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.7</machine>
|
||||
<machine maxCpus='128'>xenfv</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.3</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.4</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.5</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.1</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.2</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.0</machine>
|
||||
<machine maxCpus='288'>pc-q35-2.11</machine>
|
||||
<machine maxCpus='288'>q35</machine>
|
||||
<machine maxCpus='1'>xenpv</machine>
|
||||
<machine maxCpus='288'>pc-q35-2.10</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-1.7</machine>
|
||||
<machine maxCpus='288'>pc-q35-2.9</machine>
|
||||
<machine maxCpus='255'>pc-0.15</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-1.5</machine>
|
||||
<machine maxCpus='255'>pc-q35-2.7</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-1.6</machine>
|
||||
<machine maxCpus='288'>pc-q35-2.8</machine>
|
||||
<machine maxCpus='255'>pc-0.13</machine>
|
||||
<machine maxCpus='255'>pc-0.14</machine>
|
||||
<machine maxCpus='255'>pc-q35-2.4</machine>
|
||||
<machine maxCpus='255'>pc-q35-2.5</machine>
|
||||
<machine maxCpus='255'>pc-q35-2.6</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-1.4</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.10</machine>
|
||||
<machine maxCpus='255'>pc-0.11</machine>
|
||||
<machine maxCpus='255'>pc-0.12</machine>
|
||||
<machine maxCpus='255'>pc-0.10</machine>
|
||||
</domain>
|
||||
</arch>
|
||||
<features>
|
||||
<cpuselection/>
|
||||
<deviceboot/>
|
||||
<pae/>
|
||||
<nonpae/>
|
||||
<disksnapshot default='on' toggle='no'/>
|
||||
<acpi default='on' toggle='yes'/>
|
||||
<apic default='on' toggle='no'/>
|
||||
<pae/>
|
||||
<nonpae/>
|
||||
</features>
|
||||
</guest>''',
|
||||
|
||||
@ -91,29 +171,93 @@ CAPABILITIES_GUEST = {
|
||||
<arch name='x86_64'>
|
||||
<wordsize>64</wordsize>
|
||||
<emulator>/usr/bin/qemu-system-x86_64</emulator>
|
||||
<machine>pc-0.14</machine>
|
||||
<machine canonical='pc-0.14'>pc</machine>
|
||||
<machine>pc-0.13</machine>
|
||||
<machine>pc-0.12</machine>
|
||||
<machine>pc-0.11</machine>
|
||||
<machine>pc-0.10</machine>
|
||||
<machine>isapc</machine>
|
||||
<domain type='qemu'>
|
||||
</domain>
|
||||
<machine maxCpus='255'>pc-i440fx-2.11</machine>
|
||||
<machine canonical='pc-i440fx-2.11' maxCpus='255'>pc</machine>
|
||||
<machine maxCpus='1'>isapc</machine>
|
||||
<machine maxCpus='255'>pc-1.1</machine>
|
||||
<machine maxCpus='255'>pc-1.2</machine>
|
||||
<machine maxCpus='255'>pc-1.3</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.8</machine>
|
||||
<machine maxCpus='255'>pc-1.0</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.9</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.6</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.7</machine>
|
||||
<machine maxCpus='128'>xenfv</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.3</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.4</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.5</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.1</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.2</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.0</machine>
|
||||
<machine maxCpus='288'>pc-q35-2.11</machine>
|
||||
<machine canonical='pc-q35-2.11' maxCpus='288'>q35</machine>
|
||||
<machine maxCpus='1'>xenpv</machine>
|
||||
<machine maxCpus='288'>pc-q35-2.10</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-1.7</machine>
|
||||
<machine maxCpus='288'>pc-q35-2.9</machine>
|
||||
<machine maxCpus='255'>pc-0.15</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-1.5</machine>
|
||||
<machine maxCpus='255'>pc-q35-2.7</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-1.6</machine>
|
||||
<machine maxCpus='288'>pc-q35-2.8</machine>
|
||||
<machine maxCpus='255'>pc-0.13</machine>
|
||||
<machine maxCpus='255'>pc-0.14</machine>
|
||||
<machine maxCpus='255'>pc-q35-2.4</machine>
|
||||
<machine maxCpus='255'>pc-q35-2.5</machine>
|
||||
<machine maxCpus='255'>pc-q35-2.6</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-1.4</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.10</machine>
|
||||
<machine maxCpus='255'>pc-0.11</machine>
|
||||
<machine maxCpus='255'>pc-0.12</machine>
|
||||
<machine maxCpus='255'>pc-0.10</machine>
|
||||
<domain type='qemu'/>
|
||||
<domain type='kvm'>
|
||||
<emulator>/usr/bin/kvm</emulator>
|
||||
<machine>pc-0.14</machine>
|
||||
<machine canonical='pc-0.14'>pc</machine>
|
||||
<machine>pc-0.13</machine>
|
||||
<machine>pc-0.12</machine>
|
||||
<machine>pc-0.11</machine>
|
||||
<machine>pc-0.10</machine>
|
||||
<machine>isapc</machine>
|
||||
<emulator>/usr/bin/qemu-kvm</emulator>
|
||||
<machine maxCpus='255'>pc-i440fx-2.11</machine>
|
||||
<machine canonical='pc-i440fx-2.11' maxCpus='255'>pc</machine>
|
||||
<machine maxCpus='1'>isapc</machine>
|
||||
<machine maxCpus='255'>pc-1.1</machine>
|
||||
<machine maxCpus='255'>pc-1.2</machine>
|
||||
<machine maxCpus='255'>pc-1.3</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.8</machine>
|
||||
<machine maxCpus='255'>pc-1.0</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.9</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.6</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.7</machine>
|
||||
<machine maxCpus='128'>xenfv</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.3</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.4</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.5</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.1</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.2</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.0</machine>
|
||||
<machine maxCpus='288'>pc-q35-2.11</machine>
|
||||
<machine canonical='pc-q35-2.11' maxCpus='288'>q35</machine>
|
||||
<machine maxCpus='1'>xenpv</machine>
|
||||
<machine maxCpus='288'>pc-q35-2.10</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-1.7</machine>
|
||||
<machine maxCpus='288'>pc-q35-2.9</machine>
|
||||
<machine maxCpus='255'>pc-0.15</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-1.5</machine>
|
||||
<machine maxCpus='255'>pc-q35-2.7</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-1.6</machine>
|
||||
<machine maxCpus='288'>pc-q35-2.8</machine>
|
||||
<machine maxCpus='255'>pc-0.13</machine>
|
||||
<machine maxCpus='255'>pc-0.14</machine>
|
||||
<machine maxCpus='255'>pc-q35-2.4</machine>
|
||||
<machine maxCpus='255'>pc-q35-2.5</machine>
|
||||
<machine maxCpus='255'>pc-q35-2.6</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-1.4</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.10</machine>
|
||||
<machine maxCpus='255'>pc-0.11</machine>
|
||||
<machine maxCpus='255'>pc-0.12</machine>
|
||||
<machine maxCpus='255'>pc-0.10</machine>
|
||||
</domain>
|
||||
</arch>
|
||||
<features>
|
||||
<cpuselection/>
|
||||
<deviceboot/>
|
||||
<disksnapshot default='on' toggle='no'/>
|
||||
<acpi default='on' toggle='yes'/>
|
||||
<apic default='on' toggle='no'/>
|
||||
</features>
|
||||
@ -232,7 +376,6 @@ CAPABILITIES_GUEST = {
|
||||
<machine>g3beige</machine>
|
||||
<machine>virtex-ml507</machine>
|
||||
<machine>mpc8544ds</machine>
|
||||
<machine canonical='bamboo-0.13'>bamboo</machine>
|
||||
<machine>bamboo-0.13</machine>
|
||||
<machine>bamboo-0.12</machine>
|
||||
<machine>ref405ep</machine>
|
||||
@ -838,6 +981,11 @@ STATIC_DOMCAPABILITIES = {
|
||||
Architecture.I686: DOMCAPABILITIES_I686
|
||||
}
|
||||
|
||||
# NOTE(aspiers): see the above note for CAPABILITIES_GUEST which
|
||||
# explains why the <machine> element here needs to be parametrised.
|
||||
#
|
||||
# The <features> element needs to be parametrised for emulating
|
||||
# environments with and without the SEV feature.
|
||||
DOMCAPABILITIES_X86_64_TEMPLATE = """
|
||||
<domainCapabilities>
|
||||
<path>/usr/bin/qemu-kvm</path>
|
||||
|
@ -1366,9 +1366,10 @@ class Connection(object):
|
||||
return fake_libvirt_data.STATIC_DOMCAPABILITIES[arch]
|
||||
|
||||
if arch == 'x86_64':
|
||||
aliases = {'pc': 'pc-i440fx-2.11', 'q35': 'pc-q35-2.11'}
|
||||
return fake_libvirt_data.DOMCAPABILITIES_X86_64_TEMPLATE % \
|
||||
{'features': self._domain_capability_features,
|
||||
'mtype': machine_type}
|
||||
'mtype': aliases.get(machine_type, machine_type)}
|
||||
|
||||
raise Exception("fakelibvirt doesn't support getDomainCapabilities "
|
||||
"for %s architecture" % arch)
|
||||
|
@ -111,22 +111,6 @@ class LibvirtConfigCapsTest(LibvirtConfigBaseTest):
|
||||
</cells>
|
||||
</topology>
|
||||
</host>
|
||||
<guest>
|
||||
<os_type>hvm</os_type>
|
||||
<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'>
|
||||
<emulator>/usr/bin/qemu-system-i386</emulator>
|
||||
</arch>
|
||||
</guest>
|
||||
</capabilities>"""
|
||||
|
||||
obj = config.LibvirtConfigCaps()
|
||||
@ -134,17 +118,6 @@ 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()
|
||||
|
||||
@ -172,6 +145,95 @@ class LibvirtConfigCapsTest(LibvirtConfigBaseTest):
|
||||
self.assertEqual(128, obj.memory)
|
||||
self.assertEqual(0, len(obj.cpus))
|
||||
|
||||
def test_config_guest(self):
|
||||
xmlin = """
|
||||
<capabilities>
|
||||
<guest>
|
||||
<os_type>hvm</os_type>
|
||||
<arch name='x86_64'>
|
||||
<emulator>/usr/bin/qemu-system-x86_64</emulator>
|
||||
<machine maxCpus='255'>pc-i440fx-2.11</machine>
|
||||
<machine canonical='pc-i440fx-2.11' maxCpus='255'>pc</machine>
|
||||
<machine maxCpus='1'>isapc</machine>
|
||||
<machine maxCpus='255'>pc-1.1</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.0</machine>
|
||||
<machine maxCpus='288'>pc-q35-2.11</machine>
|
||||
<machine canonical='pc-q35-2.11' maxCpus='288'>q35</machine>
|
||||
<machine maxCpus='1'>xenpv</machine>
|
||||
<machine maxCpus='288'>pc-q35-2.10</machine>
|
||||
<domain type="qemu" />
|
||||
<domain type="kvm">
|
||||
<emulator>/usr/bin/qemu-kvm</emulator>
|
||||
<machine maxCpus='255'>pc-i440fx-2.11</machine>
|
||||
<machine canonical='pc-i440fx-2.11' maxCpus='255'>pc</machine>
|
||||
<machine maxCpus='1'>isapc</machine>
|
||||
<machine maxCpus='255'>pc-1.1</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.0</machine>
|
||||
<machine maxCpus='288'>pc-q35-2.11</machine>
|
||||
<machine canonical='pc-q35-2.11' maxCpus='288'>q35</machine>
|
||||
<machine maxCpus='1'>xenpv</machine>
|
||||
<machine maxCpus='288'>pc-q35-2.10</machine>
|
||||
</domain>
|
||||
</arch>
|
||||
</guest>
|
||||
<guest>
|
||||
<os_type>hvm</os_type>
|
||||
<arch name='i686'>
|
||||
<emulator>/usr/bin/qemu-system-i386</emulator>
|
||||
<machine maxCpus='255'>pc-i440fx-2.11</machine>
|
||||
<machine canonical='pc-i440fx-2.11' maxCpus='255'>pc</machine>
|
||||
<machine maxCpus='1'>isapc</machine>
|
||||
<machine maxCpus='255'>pc-1.1</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.0</machine>
|
||||
<machine maxCpus='288'>pc-q35-2.11</machine>
|
||||
<machine canonical='pc-q35-2.11' maxCpus='288'>q35</machine>
|
||||
<machine maxCpus='1'>xenpv</machine>
|
||||
<machine maxCpus='288'>pc-q35-2.10</machine>
|
||||
<domain type="qemu" />
|
||||
<domain type="kvm">
|
||||
<emulator>/usr/bin/qemu-kvm</emulator>
|
||||
<machine maxCpus='255'>pc-i440fx-2.11</machine>
|
||||
<machine canonical='pc-i440fx-2.11' maxCpus='255'>pc</machine>
|
||||
<machine maxCpus='1'>isapc</machine>
|
||||
<machine maxCpus='255'>pc-1.1</machine>
|
||||
<machine maxCpus='255'>pc-i440fx-2.0</machine>
|
||||
<machine maxCpus='288'>pc-q35-2.11</machine>
|
||||
<machine canonical='pc-q35-2.11' maxCpus='288'>q35</machine>
|
||||
<machine maxCpus='1'>xenpv</machine>
|
||||
<machine maxCpus='288'>pc-q35-2.10</machine>
|
||||
</domain>
|
||||
</arch>
|
||||
</guest>
|
||||
</capabilities>"""
|
||||
obj = config.LibvirtConfigCaps()
|
||||
obj.parse_str(xmlin)
|
||||
|
||||
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)
|
||||
|
||||
guest = obj.guests[0]
|
||||
self.assertIn('qemu', guest.domains)
|
||||
self.assertIn('kvm', guest.domains)
|
||||
self.assertEqual('qemu', guest.default_domain.domtype)
|
||||
self.assertEqual('/usr/bin/qemu-system-x86_64',
|
||||
guest.default_domain.emulator)
|
||||
self.assertEqual(guest.default_domain, guest.domains['qemu'])
|
||||
for domtype, domain in guest.domains.items():
|
||||
self.assertEqual(7, len(domain.machines))
|
||||
self.assertIn('pc-i440fx-2.0', domain.machines)
|
||||
self.assertIn('xenpv', domain.machines)
|
||||
self.assertEqual(2, len(domain.aliases))
|
||||
self.assertIn('pc', domain.aliases)
|
||||
self.assertIn('q35', domain.aliases)
|
||||
|
||||
xmlout = obj.to_xml()
|
||||
self.assertXmlEqual(xmlin, xmlout, allow_mixed_nodes=True)
|
||||
|
||||
|
||||
class LibvirtConfigGuestTimerTest(LibvirtConfigBaseTest):
|
||||
def test_config_platform(self):
|
||||
|
@ -15449,13 +15449,13 @@ class LibvirtConnTestCase(test.NoDBTestCase,
|
||||
guest = vconfig.LibvirtConfigCapsGuest()
|
||||
guest.ostype = fields.VMMode.HVM
|
||||
guest.arch = fields.Architecture.X86_64
|
||||
guest.domtype = ["kvm"]
|
||||
guest.domains['kvm'] = vconfig.LibvirtConfigCapsGuestDomain()
|
||||
caps.guests.append(guest)
|
||||
|
||||
guest = vconfig.LibvirtConfigCapsGuest()
|
||||
guest.ostype = fields.VMMode.HVM
|
||||
guest.arch = fields.Architecture.I686
|
||||
guest.domtype = ["kvm"]
|
||||
guest.domains['kvm'] = vconfig.LibvirtConfigCapsGuestDomain()
|
||||
caps.guests.append(guest)
|
||||
|
||||
return caps
|
||||
@ -16483,13 +16483,14 @@ class LibvirtConnTestCase(test.NoDBTestCase,
|
||||
guest = vconfig.LibvirtConfigCapsGuest()
|
||||
guest.ostype = 'hvm'
|
||||
guest.arch = fields.Architecture.X86_64
|
||||
guest.domtype = ['kvm', 'qemu']
|
||||
guest.domains['kvm'] = vconfig.LibvirtConfigCapsGuestDomain()
|
||||
guest.domains['qemu'] = vconfig.LibvirtConfigCapsGuestDomain()
|
||||
caps.guests.append(guest)
|
||||
|
||||
guest = vconfig.LibvirtConfigCapsGuest()
|
||||
guest.ostype = 'hvm'
|
||||
guest.arch = fields.Architecture.I686
|
||||
guest.domtype = ['kvm']
|
||||
guest.domains['kvm'] = vconfig.LibvirtConfigCapsGuestDomain()
|
||||
caps.guests.append(guest)
|
||||
|
||||
# Include one that is not known to nova to make sure it
|
||||
@ -16497,7 +16498,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
|
||||
guest = vconfig.LibvirtConfigCapsGuest()
|
||||
guest.ostype = 'hvm'
|
||||
guest.arch = 'itanic'
|
||||
guest.domtype = ['kvm']
|
||||
guest.domains['kvm'] = vconfig.LibvirtConfigCapsGuestDomain()
|
||||
caps.guests.append(guest)
|
||||
|
||||
return caps
|
||||
|
@ -28,6 +28,7 @@ from nova import exception
|
||||
from nova import objects
|
||||
from nova.objects import fields as obj_fields
|
||||
from nova import test
|
||||
from nova.tests.unit.virt.libvirt import fake_libvirt_data
|
||||
from nova.tests.unit.virt.libvirt import fakelibvirt
|
||||
from nova.virt import event
|
||||
from nova.virt.libvirt import config as vconfig
|
||||
@ -638,10 +639,36 @@ class HostTestCase(test.NoDBTestCase):
|
||||
self.assertIsNone(caps.host.cpu.model)
|
||||
self.assertEqual(0, len(caps.host.cpu.features))
|
||||
|
||||
def test__get_machine_types(self):
|
||||
expected = [
|
||||
# NOTE(aspiers): in the real world, i686 would probably
|
||||
# have q35 too, but our fixtures are manipulated to
|
||||
# exclude it to allow more thorough testing the our
|
||||
# canonical machine types logic is correct.
|
||||
('i686', 'qemu', ['pc']),
|
||||
('i686', 'kvm', ['pc']),
|
||||
('x86_64', 'qemu', ['pc', 'q35']),
|
||||
('x86_64', 'kvm', ['pc', 'q35']),
|
||||
('armv7l', 'qemu', ['virt']),
|
||||
# NOTE(aspiers): we're currently missing default machine
|
||||
# types for the other architectures for which we have fake
|
||||
# capabilities.
|
||||
]
|
||||
for arch, domain, expected_mach_types in expected:
|
||||
guest_xml = fake_libvirt_data.CAPABILITIES_GUEST[arch]
|
||||
guest = vconfig.LibvirtConfigCapsGuest()
|
||||
guest.parse_str(guest_xml)
|
||||
domain = guest.domains[domain]
|
||||
self.assertEqual(set(expected_mach_types),
|
||||
self.host._get_machine_types(arch, domain),
|
||||
"for arch %s domain %s" %
|
||||
(arch, domain.domtype))
|
||||
|
||||
def _test_get_domain_capabilities(self):
|
||||
caps = self.host.get_domain_capabilities()
|
||||
for arch, mtypes in caps.items():
|
||||
for mtype, dom_cap in mtypes.items():
|
||||
self.assertIsInstance(dom_cap, vconfig.LibvirtConfigDomainCaps)
|
||||
# NOTE(sean-k-mooney): this should always be true since we are
|
||||
# mapping from an arch and machine_type to a domain cap object
|
||||
# for that pair. We use 'in' to allow libvirt to expand the
|
||||
@ -649,10 +676,18 @@ class HostTestCase(test.NoDBTestCase):
|
||||
# form e.g. pc-i440fx-2.11
|
||||
self.assertIn(mtype, dom_cap.machine_type)
|
||||
self.assertIn(dom_cap.machine_type_alias, mtype)
|
||||
|
||||
# We assume we are testing with x86_64 in other parts of the code
|
||||
# so we just assert it's in the test data and return it.
|
||||
self.assertIn('x86_64', caps)
|
||||
self.assertIn('pc', caps['x86_64'])
|
||||
expected = [
|
||||
('i686', ['pc', 'pc-i440fx-2.11']),
|
||||
('x86_64', ['pc', 'pc-i440fx-2.11', 'q35', 'pc-q35-2.11']),
|
||||
]
|
||||
for arch, expected_mtypes in expected:
|
||||
self.assertIn(arch, caps)
|
||||
for mach_type in expected_mtypes:
|
||||
self.assertIn(mach_type, caps[arch], "for arch %s" % arch)
|
||||
|
||||
return caps['x86_64']['pc']
|
||||
|
||||
def test_get_domain_capabilities(self):
|
||||
@ -742,7 +777,7 @@ class HostTestCase(test.NoDBTestCase):
|
||||
|
||||
caps = self.host.get_domain_capabilities()
|
||||
|
||||
for arch, mtype in six.iteritems(archs):
|
||||
for arch, mtype in archs.items():
|
||||
self.assertIn(arch, caps)
|
||||
self.assertNotIn('pc', caps[arch])
|
||||
self.assertIn(mtype, caps[arch])
|
||||
|
@ -25,6 +25,7 @@ helpers for populating up config object instances.
|
||||
|
||||
import time
|
||||
|
||||
from collections import OrderedDict
|
||||
from lxml import etree
|
||||
from oslo_utils import strutils
|
||||
from oslo_utils import units
|
||||
@ -391,43 +392,54 @@ class LibvirtConfigCapsGuest(LibvirtConfigObject):
|
||||
|
||||
self.arch = None
|
||||
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()
|
||||
# Map domain types such as 'qemu' and 'kvm' to
|
||||
# LibvirtConfigCapsGuestDomain instances.
|
||||
self.domains = OrderedDict()
|
||||
self.default_domain = None
|
||||
|
||||
def parse_dom(self, xmldoc):
|
||||
super(LibvirtConfigCapsGuest, self).parse_dom(xmldoc)
|
||||
|
||||
for c in xmldoc:
|
||||
if c.tag == "os_type":
|
||||
self.ostype = c.text
|
||||
elif c.tag == "arch":
|
||||
self.arch = c.get("name")
|
||||
for ac in c:
|
||||
if ac.tag == "domain":
|
||||
self.parse_domain(ac)
|
||||
elif ac.tag == "emulator":
|
||||
self.emulator = ac.text
|
||||
for child in xmldoc:
|
||||
if child.tag == "os_type":
|
||||
self.ostype = child.text
|
||||
elif child.tag == "arch":
|
||||
self.parse_arch(child)
|
||||
|
||||
def parse_domain(self, domxml):
|
||||
domtype = domxml.get("type")
|
||||
self.domtype.append(domtype)
|
||||
for dc in domxml:
|
||||
if dc.tag == "emulator":
|
||||
self.domemulator[domtype] = dc.text
|
||||
def parse_arch(self, xmldoc):
|
||||
self.arch = xmldoc.get("name")
|
||||
# NOTE(aspiers): The data relating to each <domain> element
|
||||
# under <arch> (such as <emulator> and many <machine>
|
||||
# elements) is structured in a slightly odd way. There is one
|
||||
# "default" domain such as
|
||||
#
|
||||
# <domain type='qemu'/>
|
||||
#
|
||||
# which has no child elements, and all its data is provided in
|
||||
# sibling elements. Then others such as
|
||||
#
|
||||
# <domain type='kvm'>
|
||||
#
|
||||
# will have their <emulator> and <machine> elements as
|
||||
# children. So we need to handle the two cases separately.
|
||||
self.default_domain = LibvirtConfigCapsGuestDomain()
|
||||
for child in xmldoc:
|
||||
if child.tag == "domain":
|
||||
if list(child):
|
||||
# This domain has children, so create a new instance,
|
||||
# parse it, and register it in the dict of domains.
|
||||
domain = LibvirtConfigCapsGuestDomain()
|
||||
domain.parse_dom(child)
|
||||
self.domains[domain.domtype] = domain
|
||||
else:
|
||||
# This is the childless <domain/> element for the
|
||||
# default domain
|
||||
self.default_domain.parse_domain(child)
|
||||
self.domains[self.default_domain.domtype] = \
|
||||
self.default_domain
|
||||
else:
|
||||
# Sibling element of the default domain
|
||||
self.default_domain.parse_child(child)
|
||||
|
||||
def format_dom(self):
|
||||
caps = super(LibvirtConfigCapsGuest, self).format_dom()
|
||||
@ -435,20 +447,82 @@ class LibvirtConfigCapsGuest(LibvirtConfigObject):
|
||||
if self.ostype is not None:
|
||||
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)
|
||||
arch = self.format_arch()
|
||||
caps.append(arch)
|
||||
|
||||
return caps
|
||||
|
||||
def format_arch(self):
|
||||
arch = etree.Element("arch", name=self.arch)
|
||||
|
||||
for c in self.default_domain.format_dom():
|
||||
arch.append(c)
|
||||
arch.append(self._new_node("domain", type=self.default_domain.domtype))
|
||||
|
||||
for domtype, domain in self.domains.items():
|
||||
if domtype == self.default_domain.domtype:
|
||||
# We've already added this domain at the top level
|
||||
continue
|
||||
arch.append(domain.format_dom())
|
||||
|
||||
return arch
|
||||
|
||||
|
||||
class LibvirtConfigCapsGuestDomain(LibvirtConfigObject):
|
||||
def __init__(self, **kwargs):
|
||||
super(LibvirtConfigCapsGuestDomain, self).__init__(
|
||||
root_name="domain", **kwargs)
|
||||
|
||||
self.domtype = None
|
||||
|
||||
# Track <emulator> values, which we need in order to be able
|
||||
# to call virConnectGetDomainCapabilities() - typically
|
||||
# something like '/usr/bin/qemu-system-i386'.
|
||||
self.emulator = None
|
||||
|
||||
self.machines = {}
|
||||
self.aliases = {}
|
||||
|
||||
def parse_dom(self, xmldoc):
|
||||
super(LibvirtConfigCapsGuestDomain, self).parse_dom(xmldoc)
|
||||
|
||||
self.parse_domain(xmldoc)
|
||||
|
||||
for c in xmldoc:
|
||||
self.parse_child(c)
|
||||
|
||||
def parse_child(self, xmldoc):
|
||||
if xmldoc.tag == "emulator":
|
||||
self.emulator = xmldoc.text
|
||||
elif xmldoc.tag == "machine":
|
||||
self.parse_machine(xmldoc)
|
||||
|
||||
def parse_domain(self, xmldoc):
|
||||
self.domtype = xmldoc.get("type")
|
||||
if self.domtype is None:
|
||||
raise exception.InvalidInput(
|
||||
"Didn't find domain type in %s", xmldoc)
|
||||
|
||||
def parse_machine(self, xmldoc):
|
||||
if 'canonical' in xmldoc.attrib:
|
||||
self.aliases[xmldoc.text] = xmldoc.attrib
|
||||
else:
|
||||
self.machines[xmldoc.text] = xmldoc.attrib
|
||||
|
||||
def format_dom(self):
|
||||
domain = super(LibvirtConfigCapsGuestDomain, self).format_dom()
|
||||
|
||||
if self.domtype is not None:
|
||||
domain.set("type", self.domtype)
|
||||
if self.emulator is not None:
|
||||
domain.append(self._text_node("emulator", self.emulator))
|
||||
for mach_type, machine in self.machines.items():
|
||||
domain.append(self._text_node("machine", mach_type, **machine))
|
||||
for alias, machine in self.aliases.items():
|
||||
domain.append(self._text_node("machine", alias, **machine))
|
||||
|
||||
return domain
|
||||
|
||||
|
||||
class LibvirtConfigGuestTimer(LibvirtConfigObject):
|
||||
|
||||
|
@ -6057,11 +6057,11 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||
caps = self._host.get_capabilities()
|
||||
instance_caps = list()
|
||||
for g in caps.guests:
|
||||
for dt in g.domtype:
|
||||
for domain_type in g.domains:
|
||||
try:
|
||||
instance_cap = (
|
||||
fields.Architecture.canonicalize(g.arch),
|
||||
fields.HVType.canonicalize(dt),
|
||||
fields.HVType.canonicalize(domain_type),
|
||||
fields.VMMode.canonicalize(g.ostype))
|
||||
instance_caps.append(instance_cap)
|
||||
except exception.InvalidArchitectureName:
|
||||
|
@ -700,12 +700,29 @@ class Host(object):
|
||||
getDomainCapabilities() once for every single (architecture,
|
||||
machine_type) combination. In particular, SEV support isn't
|
||||
reported per-machine type, and since there are usually many
|
||||
machine types, we follow the advice of the experts that for
|
||||
now it's sufficient to call it once per host architecture:
|
||||
machine types, we heed the advice of the experts that it's
|
||||
typically sufficient to call it once per host architecture:
|
||||
|
||||
https://bugzilla.redhat.com/show_bug.cgi?id=1683471#c7
|
||||
|
||||
However, future domain capabilities might report SEV in a more
|
||||
However, that's not quite sufficient in the context of nova,
|
||||
because SEV guests typically require a q35 machine type, as do
|
||||
KVM/QEMU guests that want Secure Boot, whereas the current
|
||||
default machine type for x86_64 is 'pc'. So we need results
|
||||
from the getDomainCapabilities API for at least those two.
|
||||
Fortunately we can take advantage of the results from the
|
||||
getCapabilities API which marks selected machine types as
|
||||
canonical, e.g.:
|
||||
|
||||
<machine canonical='pc-i440fx-2.11' maxCpus='255'>pc</machine>
|
||||
<machine canonical='pc-q35-2.11' maxCpus='288'>q35</machine>
|
||||
|
||||
So for now, we call getDomainCapabilities for these canonical
|
||||
machine types of each architecture, plus for the
|
||||
architecture's default machine type, if that is not one of the
|
||||
canonical types.
|
||||
|
||||
Future domain capabilities might report SEV in a more
|
||||
fine-grained manner, and we also expect to use this method to
|
||||
detect other features, such as for gracefully handling machine
|
||||
types and potentially for detecting OVMF binaries. Therefore
|
||||
@ -741,90 +758,22 @@ class Host(object):
|
||||
|
||||
for guest in caps.guests:
|
||||
arch = guest.arch
|
||||
machine_type = \
|
||||
libvirt_utils.get_default_machine_type(arch)
|
||||
domain = guest.domains.get(virt_type, guest.default_domain)
|
||||
machine_types = self._get_machine_types(arch, domain)
|
||||
|
||||
emulator_bin = guest.emulator
|
||||
if virt_type in guest.domemulator:
|
||||
emulator_bin = guest.domemulator[virt_type]
|
||||
|
||||
# It is expected that each <guest> will have a different
|
||||
# architecture, but it doesn't hurt to add a safety net to
|
||||
# avoid needlessly calling libvirt's API more times than
|
||||
# we need.
|
||||
if machine_type and machine_type in domain_caps[arch]:
|
||||
continue
|
||||
|
||||
# NOTE(aspiers): machine_type could be None here if nova
|
||||
# doesn't have a default machine type for this
|
||||
# architecture. In that case we pass a machine_type of
|
||||
# None to the libvirt API and rely on it choosing a
|
||||
# sensible default which will be returned in the <machine>
|
||||
# element. It could also be an alias like 'pc' rather
|
||||
# than a full machine type.
|
||||
#
|
||||
# NOTE(kchamart): Prior to libvirt v4.7.0 libvirt picked
|
||||
# its default machine type for x86, 'pc', as reported by
|
||||
# QEMU's default. From libvirt v4.7.0 onwards, libvirt
|
||||
# _explicitly_ declared the "preferred" default for x86 as
|
||||
# 'pc' (and appropriate values for other architectures),
|
||||
# and only uses QEMU's reported default (whatever that may
|
||||
# be) if 'pc' does not exist. This was done "to isolate
|
||||
# applications from hypervisor changes that may cause
|
||||
# incompatibilities" -- i.e. if, or when, QEMU changes its
|
||||
# default machine type to something else. Refer to this
|
||||
# libvirt commit:
|
||||
#
|
||||
# https://libvirt.org/git/?p=libvirt.git;a=commit;h=26cfb1a3
|
||||
try:
|
||||
cap_obj = self._get_domain_capabilities(
|
||||
emulator_bin=emulator_bin, arch=arch,
|
||||
machine_type=machine_type, virt_type=virt_type)
|
||||
except libvirt.libvirtError as ex:
|
||||
# NOTE(sean-k-mooney): This can happen for several
|
||||
# reasons, but one common example is if you have
|
||||
# multiple QEMU emulators installed and you set
|
||||
# virt-type=kvm. In this case any non-native emulator,
|
||||
# e.g. AArch64 on an x86 host, will (correctly) raise
|
||||
# an exception as KVM cannot be used to accelerate CPU
|
||||
# instructions for non-native architectures.
|
||||
error_code = ex.get_error_code()
|
||||
LOG.debug(
|
||||
"Error from libvirt when retrieving domain capabilities "
|
||||
"for arch %(arch)s / virt_type %(virt_type)s / "
|
||||
"machine_type %(mach_type)s: "
|
||||
"[Error Code %(error_code)s]: %(exception)s",
|
||||
{'arch': arch, 'virt_type': virt_type,
|
||||
'mach_type': machine_type, 'error_code': error_code,
|
||||
'exception': ex})
|
||||
# Remove archs added by default dict lookup when checking
|
||||
# if the machine type has already been recoded.
|
||||
if arch in domain_caps:
|
||||
domain_caps.pop(arch)
|
||||
continue
|
||||
|
||||
# Register the domain caps using the expanded form of
|
||||
# machine type returned by libvirt in the <machine>
|
||||
# element (e.g. pc-i440fx-2.11)
|
||||
if cap_obj.machine_type:
|
||||
domain_caps[arch][cap_obj.machine_type] = cap_obj
|
||||
else:
|
||||
# NOTE(aspiers): In theory this should never happen,
|
||||
# but better safe than sorry.
|
||||
LOG.warning(
|
||||
"libvirt getDomainCapabilities("
|
||||
"emulator_bin=%(emulator_bin)s, arch=%(arch)s, "
|
||||
"machine_type=%(machine_type)s, virt_type=%(virt_type)s) "
|
||||
"returned null <machine> type",
|
||||
{'emulator_bin': emulator_bin, 'arch': arch,
|
||||
'machine_type': machine_type, 'virt_type': virt_type}
|
||||
)
|
||||
|
||||
# And if we passed an alias, register the domain caps
|
||||
# under that too.
|
||||
if machine_type and machine_type != cap_obj.machine_type:
|
||||
domain_caps[arch][machine_type] = cap_obj
|
||||
cap_obj.machine_type_alias = machine_type
|
||||
for machine_type in machine_types:
|
||||
# It is expected that if there are multiple <guest>
|
||||
# elements, each will have a different architecture;
|
||||
# for example, on x86 hosts one <guest> will contain
|
||||
# <arch name='i686'> and one will contain <arch
|
||||
# name='x86_64'>. But it doesn't hurt to add a safety
|
||||
# net to avoid needlessly calling libvirt's API more
|
||||
# times than we need.
|
||||
if machine_type and machine_type in domain_caps[arch]:
|
||||
continue
|
||||
self._add_to_domain_capabilities(domain.emulator, arch,
|
||||
domain_caps, machine_type,
|
||||
virt_type)
|
||||
|
||||
# NOTE(aspiers): Use a temporary variable to update the
|
||||
# instance variable atomically, otherwise if some API
|
||||
@ -834,9 +783,98 @@ class Host(object):
|
||||
|
||||
return self._domain_caps
|
||||
|
||||
def _get_domain_capabilities(self, emulator_bin=None, arch=None,
|
||||
machine_type=None, virt_type=None, flags=0):
|
||||
def _get_machine_types(self, arch, domain):
|
||||
"""Get the machine types for this architecture for which we need to
|
||||
call getDomainCapabilities, i.e. the canonical machine types,
|
||||
and the default machine type (if it's not one of the canonical
|
||||
machine types).
|
||||
|
||||
See the docstring for get_domain_capabilities() for an explanation
|
||||
of why we choose this set of machine types.
|
||||
"""
|
||||
# NOTE(aspiers): machine_type could be None here if nova
|
||||
# doesn't have a default machine type for this architecture.
|
||||
# See _add_to_domain_capabilities() below for how this is handled.
|
||||
mtypes = set([libvirt_utils.get_default_machine_type(arch)])
|
||||
mtypes.update(domain.aliases.keys())
|
||||
LOG.debug("Getting domain capabilities for %(arch)s via "
|
||||
"machine types: %(mtypes)s",
|
||||
{'arch': arch, 'mtypes': mtypes})
|
||||
return mtypes
|
||||
|
||||
def _add_to_domain_capabilities(self, emulator_bin, arch, domain_caps,
|
||||
machine_type, virt_type):
|
||||
# NOTE(aspiers): machine_type could be None here if nova
|
||||
# doesn't have a default machine type for this architecture.
|
||||
# In that case we pass a machine_type of None to the libvirt
|
||||
# API and rely on it choosing a sensible default which will be
|
||||
# returned in the <machine> element. It could also be an
|
||||
# alias like 'pc' rather than a full machine type.
|
||||
#
|
||||
# NOTE(kchamart): Prior to libvirt v4.7.0 libvirt picked its
|
||||
# default machine type for x86, 'pc', as reported by QEMU's
|
||||
# default. From libvirt v4.7.0 onwards, libvirt _explicitly_
|
||||
# declared the "preferred" default for x86 as 'pc' (and
|
||||
# appropriate values for other architectures), and only uses
|
||||
# QEMU's reported default (whatever that may be) if 'pc' does
|
||||
# not exist. This was done "to isolate applications from
|
||||
# hypervisor changes that may cause incompatibilities" --
|
||||
# i.e. if, or when, QEMU changes its default machine type to
|
||||
# something else. Refer to this libvirt commit:
|
||||
#
|
||||
# https://libvirt.org/git/?p=libvirt.git;a=commit;h=26cfb1a3
|
||||
try:
|
||||
cap_obj = self._get_domain_capabilities(
|
||||
emulator_bin=emulator_bin, arch=arch,
|
||||
machine_type=machine_type, virt_type=virt_type)
|
||||
except libvirt.libvirtError as ex:
|
||||
# NOTE(sean-k-mooney): This can happen for several
|
||||
# reasons, but one common example is if you have
|
||||
# multiple QEMU emulators installed and you set
|
||||
# virt-type=kvm. In this case any non-native emulator,
|
||||
# e.g. AArch64 on an x86 host, will (correctly) raise
|
||||
# an exception as KVM cannot be used to accelerate CPU
|
||||
# instructions for non-native architectures.
|
||||
error_code = ex.get_error_code()
|
||||
LOG.debug(
|
||||
"Error from libvirt when retrieving domain capabilities "
|
||||
"for arch %(arch)s / virt_type %(virt_type)s / "
|
||||
"machine_type %(mach_type)s: "
|
||||
"[Error Code %(error_code)s]: %(exception)s",
|
||||
{'arch': arch, 'virt_type': virt_type,
|
||||
'mach_type': machine_type, 'error_code': error_code,
|
||||
'exception': ex})
|
||||
# Remove archs added by default dict lookup when checking
|
||||
# if the machine type has already been recoded.
|
||||
if arch in domain_caps:
|
||||
domain_caps.pop(arch)
|
||||
return
|
||||
|
||||
# Register the domain caps using the expanded form of
|
||||
# machine type returned by libvirt in the <machine>
|
||||
# element (e.g. pc-i440fx-2.11)
|
||||
if cap_obj.machine_type:
|
||||
domain_caps[arch][cap_obj.machine_type] = cap_obj
|
||||
else:
|
||||
# NOTE(aspiers): In theory this should never happen,
|
||||
# but better safe than sorry.
|
||||
LOG.warning(
|
||||
"libvirt getDomainCapabilities("
|
||||
"emulator_bin=%(emulator_bin)s, arch=%(arch)s, "
|
||||
"machine_type=%(machine_type)s, virt_type=%(virt_type)s) "
|
||||
"returned null <machine> type",
|
||||
{'emulator_bin': emulator_bin, 'arch': arch,
|
||||
'machine_type': machine_type, 'virt_type': virt_type}
|
||||
)
|
||||
|
||||
# And if we passed an alias, register the domain caps
|
||||
# under that too.
|
||||
if machine_type and machine_type != cap_obj.machine_type:
|
||||
domain_caps[arch][machine_type] = cap_obj
|
||||
cap_obj.machine_type_alias = machine_type
|
||||
|
||||
def _get_domain_capabilities(self, emulator_bin=None, arch=None,
|
||||
machine_type=None, virt_type=None, flags=0):
|
||||
xmlstr = self.get_connection().getDomainCapabilities(
|
||||
emulator_bin,
|
||||
arch,
|
||||
|
Loading…
x
Reference in New Issue
Block a user