diff --git a/nova/scheduler/utils.py b/nova/scheduler/utils.py index 961ef93e30..daeb2638e0 100644 --- a/nova/scheduler/utils.py +++ b/nova/scheduler/utils.py @@ -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 diff --git a/nova/tests/fixtures/libvirt.py b/nova/tests/fixtures/libvirt.py index 35a11547cf..3784f1005c 100644 --- a/nova/tests/fixtures/libvirt.py +++ b/nova/tests/fixtures/libvirt.py @@ -2122,7 +2122,6 @@ class Connection(object): tpm-tis - tpm-crb passthrough diff --git a/nova/tests/unit/scheduler/test_utils.py b/nova/tests/unit/scheduler/test_utils.py index d7382228e8..41a6445b99 100644 --- a/nova/tests/unit/scheduler/test_utils.py +++ b/nova/tests/unit/scheduler/test_utils.py @@ -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, diff --git a/nova/tests/unit/virt/libvirt/test_driver.py b/nova/tests/unit/virt/libvirt/test_driver.py index 8046a4f7ef..c91c01fe60 100644 --- a/nova/tests/unit/virt/libvirt/test_driver.py +++ b/nova/tests/unit/virt/libvirt/test_driver.py @@ -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.' diff --git a/nova/tests/unit/virt/libvirt/test_host.py b/nova/tests/unit/virt/libvirt/test_host.py index 0bfc6103cb..00ac58aca1 100644 --- a/nova/tests/unit/virt/libvirt/test_host.py +++ b/nova/tests/unit/virt/libvirt/test_host.py @@ -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( diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py index e90c628454..78ed7c9ed8 100644 --- a/nova/virt/libvirt/driver.py +++ b/nova/virt/libvirt/driver.py @@ -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. diff --git a/nova/virt/libvirt/host.py b/nova/virt/libvirt/host.py index e8d066c4ec..50ff62fa13 100644 --- a/nova/virt/libvirt/host.py +++ b/nova/virt/libvirt/host.py @@ -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) diff --git a/requirements.txt b/requirements.txt index eb158af1c0..430e6e96d1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -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