libvirt: Report available TPM models

Libvirt 8.0.0 introduced the new domain capabilities API filed to
present available TPM models. This introduces the logic to report
available TPM models as compute node traits, and use that trait for
scheduling to ensure a request tpm model is available at the compute
node where the instance is being launched.

Depends-on: https://review.opendev.org/c/openstack/os-traits/+/909107
Implements: blueprint libvirt-detect-vtpm-support
Change-Id: Iec98e7b0d19f37f094152a61a26790fcdf3328d9
This commit is contained in:
Takashi Kajinami
2024-02-16 15:43:07 +09:00
parent 510cc2404e
commit c8674a88b6
8 changed files with 107 additions and 23 deletions
+13 -7
View File
@@ -295,14 +295,20 @@ class ResourceRequest(object):
if not vtpm_config:
return
# Require the appropriate vTPM version support trait on a host.
if vtpm_config.version == obj_fields.TPMVersion.v1_2:
trait = os_traits.COMPUTE_SECURITY_TPM_1_2
else:
trait = os_traits.COMPUTE_SECURITY_TPM_2_0
# Require the appropriate vTPM model support trait on a host.
model_trait = os_traits.COMPUTE_SECURITY_TPM_TIS
if vtpm_config.model == obj_fields.TPMModel.CRB:
model_trait = os_traits.COMPUTE_SECURITY_TPM_CRB
self._add_trait(trait, 'required')
LOG.debug("Requiring emulated TPM support via trait %s.", trait)
# Require the appropriate vTPM version support trait on a host.
version_trait = os_traits.COMPUTE_SECURITY_TPM_1_2
if vtpm_config.version == obj_fields.TPMVersion.v2_0:
version_trait = os_traits.COMPUTE_SECURITY_TPM_2_0
self._add_trait(model_trait, 'required')
self._add_trait(version_trait, 'required')
LOG.debug("Requiring emulated TPM support via trait %s and %s.",
version_trait, model_trait)
def _translate_memory_encryption(self, flavor, image):
"""When the hw:mem_encryption extra spec or the hw_mem_encryption
-1
View File
@@ -2122,7 +2122,6 @@ class Connection(object):
<tpm supported='yes'>
<enum name='model'>
<value>tpm-tis</value>
<value>tpm-crb</value>
</enum>
<enum name='backendModel'>
<value>passthrough</value>
+35 -2
View File
@@ -1308,6 +1308,33 @@ class TestUtils(TestUtilsBase):
rr = utils.ResourceRequest.from_request_spec(rs)
self.assertResourceRequestsEqual(expected, rr)
def test_resource_request_from_request_spec_with_vtpm_version_only(self):
flavor = objects.Flavor(
vcpus=1, memory_mb=1024, root_gb=10, ephemeral_gb=5, swap=0,
extra_specs={'hw:tpm_version': '1.2'},
)
image = objects.ImageMeta(
properties=objects.ImageMetaProps(
hw_tpm_version='1.2',
)
)
expected = FakeResourceRequest()
expected._rg_by_id[None] = objects.RequestGroup(
use_same_provider=False,
required_traits={
'COMPUTE_SECURITY_TPM_1_2',
'COMPUTE_SECURITY_TPM_TIS',
},
resources={
'VCPU': 1,
'MEMORY_MB': 1024,
'DISK_GB': 15,
},
)
rs = objects.RequestSpec(flavor=flavor, image=image, is_bfv=False)
rr = utils.ResourceRequest.from_request_spec(rs)
self.assertResourceRequestsEqual(expected, rr)
def test_resource_request_from_request_spec_with_vtpm_1_2(self):
flavor = objects.Flavor(
vcpus=1, memory_mb=1024, root_gb=10, ephemeral_gb=5, swap=0,
@@ -1322,7 +1349,10 @@ class TestUtils(TestUtilsBase):
expected = FakeResourceRequest()
expected._rg_by_id[None] = objects.RequestGroup(
use_same_provider=False,
required_traits={'COMPUTE_SECURITY_TPM_1_2'},
required_traits={
'COMPUTE_SECURITY_TPM_1_2',
'COMPUTE_SECURITY_TPM_TIS',
},
resources={
'VCPU': 1,
'MEMORY_MB': 1024,
@@ -1347,7 +1377,10 @@ class TestUtils(TestUtilsBase):
expected = FakeResourceRequest()
expected._rg_by_id[None] = objects.RequestGroup(
use_same_provider=False,
required_traits={'COMPUTE_SECURITY_TPM_2_0'},
required_traits={
'COMPUTE_SECURITY_TPM_2_0',
'COMPUTE_SECURITY_TPM_CRB',
},
resources={
'VCPU': 1,
'MEMORY_MB': 1024,
+12 -4
View File
@@ -997,6 +997,8 @@ class LibvirtConnTestCase(test.NoDBTestCase,
'COMPUTE_NET_VIRTIO_PACKED': True,
'COMPUTE_SECURITY_TPM_1_2': False,
'COMPUTE_SECURITY_TPM_2_0': False,
'COMPUTE_SECURITY_TPM_TIS': False,
'COMPUTE_SECURITY_TPM_CRB': False,
'COMPUTE_STORAGE_BUS_VIRTIO': True,
'COMPUTE_VIOMMU_MODEL_AUTO': True,
'COMPUTE_VIOMMU_MODEL_INTEL': True,
@@ -1049,6 +1051,8 @@ class LibvirtConnTestCase(test.NoDBTestCase,
'COMPUTE_NET_VIRTIO_PACKED': True,
'COMPUTE_SECURITY_TPM_1_2': False,
'COMPUTE_SECURITY_TPM_2_0': False,
'COMPUTE_SECURITY_TPM_TIS': False,
'COMPUTE_SECURITY_TPM_CRB': False,
'COMPUTE_VIOMMU_MODEL_AUTO': True,
'COMPUTE_VIOMMU_MODEL_INTEL': True,
'COMPUTE_VIOMMU_MODEL_SMMUV3': True,
@@ -22506,7 +22510,9 @@ class TestUpdateProviderTree(test.NoDBTestCase):
def test_update_provider_tree_with_tpm_traits(self):
self.flags(swtpm_enabled=True, group='libvirt')
self._test_update_provider_tree()
for trait in ('COMPUTE_SECURITY_TPM_2_0', 'COMPUTE_SECURITY_TPM_1_2'):
for trait in (
'COMPUTE_SECURITY_TPM_TIS', 'COMPUTE_SECURITY_TPM_CRB',
'COMPUTE_SECURITY_TPM_2_0', 'COMPUTE_SECURITY_TPM_1_2'):
self.assertIn(trait, self.pt.data(self.cn_rp['uuid']).traits)
@mock.patch.object(
@@ -22516,7 +22522,9 @@ class TestUpdateProviderTree(test.NoDBTestCase):
def test_update_provider_tree_with_tpm_traits_supported(self):
self.flags(swtpm_enabled=True, group='libvirt')
self._test_update_provider_tree()
for trait in ('COMPUTE_SECURITY_TPM_2_0', 'COMPUTE_SECURITY_TPM_1_2'):
for trait in (
'COMPUTE_SECURITY_TPM_TIS', 'COMPUTE_SECURITY_TPM_CRB',
'COMPUTE_SECURITY_TPM_2_0', 'COMPUTE_SECURITY_TPM_1_2'):
self.assertIn(trait, self.pt.data(self.cn_rp['uuid']).traits)
@mock.patch.object(
@@ -22527,9 +22535,9 @@ class TestUpdateProviderTree(test.NoDBTestCase):
def test_update_provider_tree_with_tpm_traits_versions(self):
self.flags(swtpm_enabled=True, group='libvirt')
self._test_update_provider_tree()
for trait in ('COMPUTE_SECURITY_TPM_2_0',):
for trait in ('COMPUTE_SECURITY_TPM_TIS', 'COMPUTE_SECURITY_TPM_2_0'):
self.assertIn(trait, self.pt.data(self.cn_rp['uuid']).traits)
for trait in ('COMPUTE_SECURITY_TPM_1_2',):
for trait in ('COMPUTE_SECURITY_TPM_CRB', 'COMPUTE_SECURITY_TPM_1_2',):
self.assertNotIn(trait, self.pt.data(self.cn_rp['uuid']).traits)
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver.'
+1 -1
View File
@@ -920,7 +920,7 @@ class HostTestCase(test.NoDBTestCase):
type(caps.devices.tpm))
self.assertTrue(caps.devices.tpm.supported)
self.assertEqual(
['tpm-tis', 'tpm-crb'],
['tpm-tis'],
caps.devices.tpm.models
)
self.assertEqual(
+27 -7
View File
@@ -12902,23 +12902,43 @@ class LibvirtDriver(driver.ComputeDriver):
return {
ot.COMPUTE_SECURITY_TPM_2_0: False,
ot.COMPUTE_SECURITY_TPM_1_2: False,
ot.COMPUTE_SECURITY_TPM_TIS: False,
ot.COMPUTE_SECURITY_TPM_CRB: False,
}
tpm_models = self._host.tpm_models
tpm_versions = self._host.tpm_versions
# libvirt < 8.6 does not provide supported versions in domain
# capabilities
# TODO(tkajinam): Remove this once libvirt>=8.6.0 is required.
tr = {}
if tpm_models is None:
# TODO(tkajinam): Remove this fallback once libvirt>=8.0.0 is
# required.
tr.update({
ot.COMPUTE_SECURITY_TPM_TIS: True,
ot.COMPUTE_SECURITY_TPM_CRB: True,
})
else:
tr.update({
ot.COMPUTE_SECURITY_TPM_TIS: 'tpm-tis' in tpm_models,
ot.COMPUTE_SECURITY_TPM_CRB: 'tpm-crb' in tpm_models,
})
if tpm_versions is None:
return {
# TODO(tkajinam): Remove this fallback once libvirt>=8.6.0 is
# required.
tr.update({
ot.COMPUTE_SECURITY_TPM_2_0: True,
ot.COMPUTE_SECURITY_TPM_1_2: True,
}
})
else:
tr.update({
ot.COMPUTE_SECURITY_TPM_2_0: '2.0' in tpm_versions,
ot.COMPUTE_SECURITY_TPM_1_2: '1.2' in tpm_versions,
})
return {
ot.COMPUTE_SECURITY_TPM_2_0: '2.0' in tpm_versions,
ot.COMPUTE_SECURITY_TPM_1_2: '1.2' in tpm_versions,
}
return tr
def _get_vif_model_traits(self) -> ty.Dict[str, bool]:
"""Get vif model traits based on the currently enabled virt_type.
+18
View File
@@ -1884,6 +1884,24 @@ class Host(object):
# safe guard
return []
@property
def tpm_models(self) -> ty.Optional[ty.List[str]]:
# we only check the host architecture and the first machine type
# because vtpm support is independent from cpu architecture
arch = self.get_capabilities().host.cpu.arch
domain_caps = self.get_domain_capabilities()
for machine_type in domain_caps[arch]:
_tpm = domain_caps[arch][machine_type].devices.tpm
# TODO(tkajinam): Remove first check once libvirt >= 8.0.0 is
# required
# TODO(tkajinam): Remove second check once libvirt >= 8.6.0 is
# required
if _tpm is None or _tpm.models is None:
return None
return _tpm.models
# safe guard
return []
def _kernel_supports_amd_sev(self) -> bool:
if not os.path.exists(SEV_KERNEL_PARAM_FILE):
LOG.debug("%s does not exist", SEV_KERNEL_PARAM_FILE)
+1 -1
View File
@@ -52,7 +52,7 @@ psutil>=3.2.2 # BSD
oslo.versionedobjects>=1.35.0 # Apache-2.0
os-brick>=5.2 # Apache-2.0
os-resource-classes>=1.1.0 # Apache-2.0
os-traits>=3.0.0 # Apache-2.0
os-traits>=3.1.0 # Apache-2.0
os-vif>=3.1.0 # Apache-2.0
castellan>=0.16.0 # Apache-2.0
microversion-parse>=0.2.1 # Apache-2.0