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:
+13
-7
@@ -295,14 +295,20 @@ class ResourceRequest(object):
|
|||||||
if not vtpm_config:
|
if not vtpm_config:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Require the appropriate vTPM version support trait on a host.
|
# Require the appropriate vTPM model support trait on a host.
|
||||||
if vtpm_config.version == obj_fields.TPMVersion.v1_2:
|
model_trait = os_traits.COMPUTE_SECURITY_TPM_TIS
|
||||||
trait = os_traits.COMPUTE_SECURITY_TPM_1_2
|
if vtpm_config.model == obj_fields.TPMModel.CRB:
|
||||||
else:
|
model_trait = os_traits.COMPUTE_SECURITY_TPM_CRB
|
||||||
trait = os_traits.COMPUTE_SECURITY_TPM_2_0
|
|
||||||
|
|
||||||
self._add_trait(trait, 'required')
|
# Require the appropriate vTPM version support trait on a host.
|
||||||
LOG.debug("Requiring emulated TPM support via trait %s.", trait)
|
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):
|
def _translate_memory_encryption(self, flavor, image):
|
||||||
"""When the hw:mem_encryption extra spec or the hw_mem_encryption
|
"""When the hw:mem_encryption extra spec or the hw_mem_encryption
|
||||||
|
|||||||
Vendored
-1
@@ -2122,7 +2122,6 @@ class Connection(object):
|
|||||||
<tpm supported='yes'>
|
<tpm supported='yes'>
|
||||||
<enum name='model'>
|
<enum name='model'>
|
||||||
<value>tpm-tis</value>
|
<value>tpm-tis</value>
|
||||||
<value>tpm-crb</value>
|
|
||||||
</enum>
|
</enum>
|
||||||
<enum name='backendModel'>
|
<enum name='backendModel'>
|
||||||
<value>passthrough</value>
|
<value>passthrough</value>
|
||||||
|
|||||||
@@ -1308,6 +1308,33 @@ class TestUtils(TestUtilsBase):
|
|||||||
rr = utils.ResourceRequest.from_request_spec(rs)
|
rr = utils.ResourceRequest.from_request_spec(rs)
|
||||||
self.assertResourceRequestsEqual(expected, rr)
|
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):
|
def test_resource_request_from_request_spec_with_vtpm_1_2(self):
|
||||||
flavor = objects.Flavor(
|
flavor = objects.Flavor(
|
||||||
vcpus=1, memory_mb=1024, root_gb=10, ephemeral_gb=5, swap=0,
|
vcpus=1, memory_mb=1024, root_gb=10, ephemeral_gb=5, swap=0,
|
||||||
@@ -1322,7 +1349,10 @@ class TestUtils(TestUtilsBase):
|
|||||||
expected = FakeResourceRequest()
|
expected = FakeResourceRequest()
|
||||||
expected._rg_by_id[None] = objects.RequestGroup(
|
expected._rg_by_id[None] = objects.RequestGroup(
|
||||||
use_same_provider=False,
|
use_same_provider=False,
|
||||||
required_traits={'COMPUTE_SECURITY_TPM_1_2'},
|
required_traits={
|
||||||
|
'COMPUTE_SECURITY_TPM_1_2',
|
||||||
|
'COMPUTE_SECURITY_TPM_TIS',
|
||||||
|
},
|
||||||
resources={
|
resources={
|
||||||
'VCPU': 1,
|
'VCPU': 1,
|
||||||
'MEMORY_MB': 1024,
|
'MEMORY_MB': 1024,
|
||||||
@@ -1347,7 +1377,10 @@ class TestUtils(TestUtilsBase):
|
|||||||
expected = FakeResourceRequest()
|
expected = FakeResourceRequest()
|
||||||
expected._rg_by_id[None] = objects.RequestGroup(
|
expected._rg_by_id[None] = objects.RequestGroup(
|
||||||
use_same_provider=False,
|
use_same_provider=False,
|
||||||
required_traits={'COMPUTE_SECURITY_TPM_2_0'},
|
required_traits={
|
||||||
|
'COMPUTE_SECURITY_TPM_2_0',
|
||||||
|
'COMPUTE_SECURITY_TPM_CRB',
|
||||||
|
},
|
||||||
resources={
|
resources={
|
||||||
'VCPU': 1,
|
'VCPU': 1,
|
||||||
'MEMORY_MB': 1024,
|
'MEMORY_MB': 1024,
|
||||||
|
|||||||
@@ -997,6 +997,8 @@ class LibvirtConnTestCase(test.NoDBTestCase,
|
|||||||
'COMPUTE_NET_VIRTIO_PACKED': True,
|
'COMPUTE_NET_VIRTIO_PACKED': True,
|
||||||
'COMPUTE_SECURITY_TPM_1_2': False,
|
'COMPUTE_SECURITY_TPM_1_2': False,
|
||||||
'COMPUTE_SECURITY_TPM_2_0': False,
|
'COMPUTE_SECURITY_TPM_2_0': False,
|
||||||
|
'COMPUTE_SECURITY_TPM_TIS': False,
|
||||||
|
'COMPUTE_SECURITY_TPM_CRB': False,
|
||||||
'COMPUTE_STORAGE_BUS_VIRTIO': True,
|
'COMPUTE_STORAGE_BUS_VIRTIO': True,
|
||||||
'COMPUTE_VIOMMU_MODEL_AUTO': True,
|
'COMPUTE_VIOMMU_MODEL_AUTO': True,
|
||||||
'COMPUTE_VIOMMU_MODEL_INTEL': True,
|
'COMPUTE_VIOMMU_MODEL_INTEL': True,
|
||||||
@@ -1049,6 +1051,8 @@ class LibvirtConnTestCase(test.NoDBTestCase,
|
|||||||
'COMPUTE_NET_VIRTIO_PACKED': True,
|
'COMPUTE_NET_VIRTIO_PACKED': True,
|
||||||
'COMPUTE_SECURITY_TPM_1_2': False,
|
'COMPUTE_SECURITY_TPM_1_2': False,
|
||||||
'COMPUTE_SECURITY_TPM_2_0': 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_AUTO': True,
|
||||||
'COMPUTE_VIOMMU_MODEL_INTEL': True,
|
'COMPUTE_VIOMMU_MODEL_INTEL': True,
|
||||||
'COMPUTE_VIOMMU_MODEL_SMMUV3': True,
|
'COMPUTE_VIOMMU_MODEL_SMMUV3': True,
|
||||||
@@ -22506,7 +22510,9 @@ class TestUpdateProviderTree(test.NoDBTestCase):
|
|||||||
def test_update_provider_tree_with_tpm_traits(self):
|
def test_update_provider_tree_with_tpm_traits(self):
|
||||||
self.flags(swtpm_enabled=True, group='libvirt')
|
self.flags(swtpm_enabled=True, group='libvirt')
|
||||||
self._test_update_provider_tree()
|
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)
|
self.assertIn(trait, self.pt.data(self.cn_rp['uuid']).traits)
|
||||||
|
|
||||||
@mock.patch.object(
|
@mock.patch.object(
|
||||||
@@ -22516,7 +22522,9 @@ class TestUpdateProviderTree(test.NoDBTestCase):
|
|||||||
def test_update_provider_tree_with_tpm_traits_supported(self):
|
def test_update_provider_tree_with_tpm_traits_supported(self):
|
||||||
self.flags(swtpm_enabled=True, group='libvirt')
|
self.flags(swtpm_enabled=True, group='libvirt')
|
||||||
self._test_update_provider_tree()
|
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)
|
self.assertIn(trait, self.pt.data(self.cn_rp['uuid']).traits)
|
||||||
|
|
||||||
@mock.patch.object(
|
@mock.patch.object(
|
||||||
@@ -22527,9 +22535,9 @@ class TestUpdateProviderTree(test.NoDBTestCase):
|
|||||||
def test_update_provider_tree_with_tpm_traits_versions(self):
|
def test_update_provider_tree_with_tpm_traits_versions(self):
|
||||||
self.flags(swtpm_enabled=True, group='libvirt')
|
self.flags(swtpm_enabled=True, group='libvirt')
|
||||||
self._test_update_provider_tree()
|
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)
|
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)
|
self.assertNotIn(trait, self.pt.data(self.cn_rp['uuid']).traits)
|
||||||
|
|
||||||
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver.'
|
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver.'
|
||||||
|
|||||||
@@ -920,7 +920,7 @@ class HostTestCase(test.NoDBTestCase):
|
|||||||
type(caps.devices.tpm))
|
type(caps.devices.tpm))
|
||||||
self.assertTrue(caps.devices.tpm.supported)
|
self.assertTrue(caps.devices.tpm.supported)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
['tpm-tis', 'tpm-crb'],
|
['tpm-tis'],
|
||||||
caps.devices.tpm.models
|
caps.devices.tpm.models
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
|||||||
@@ -12902,23 +12902,43 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||||||
return {
|
return {
|
||||||
ot.COMPUTE_SECURITY_TPM_2_0: False,
|
ot.COMPUTE_SECURITY_TPM_2_0: False,
|
||||||
ot.COMPUTE_SECURITY_TPM_1_2: 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
|
tpm_versions = self._host.tpm_versions
|
||||||
# libvirt < 8.6 does not provide supported versions in domain
|
# libvirt < 8.6 does not provide supported versions in domain
|
||||||
# capabilities
|
# 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:
|
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_2_0: True,
|
||||||
ot.COMPUTE_SECURITY_TPM_1_2: 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 {
|
return tr
|
||||||
ot.COMPUTE_SECURITY_TPM_2_0: '2.0' in tpm_versions,
|
|
||||||
ot.COMPUTE_SECURITY_TPM_1_2: '1.2' in tpm_versions,
|
|
||||||
}
|
|
||||||
|
|
||||||
def _get_vif_model_traits(self) -> ty.Dict[str, bool]:
|
def _get_vif_model_traits(self) -> ty.Dict[str, bool]:
|
||||||
"""Get vif model traits based on the currently enabled virt_type.
|
"""Get vif model traits based on the currently enabled virt_type.
|
||||||
|
|||||||
@@ -1884,6 +1884,24 @@ class Host(object):
|
|||||||
# safe guard
|
# safe guard
|
||||||
return []
|
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:
|
def _kernel_supports_amd_sev(self) -> bool:
|
||||||
if not os.path.exists(SEV_KERNEL_PARAM_FILE):
|
if not os.path.exists(SEV_KERNEL_PARAM_FILE):
|
||||||
LOG.debug("%s does not exist", SEV_KERNEL_PARAM_FILE)
|
LOG.debug("%s does not exist", SEV_KERNEL_PARAM_FILE)
|
||||||
|
|||||||
+1
-1
@@ -52,7 +52,7 @@ psutil>=3.2.2 # BSD
|
|||||||
oslo.versionedobjects>=1.35.0 # Apache-2.0
|
oslo.versionedobjects>=1.35.0 # Apache-2.0
|
||||||
os-brick>=5.2 # Apache-2.0
|
os-brick>=5.2 # Apache-2.0
|
||||||
os-resource-classes>=1.1.0 # 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
|
os-vif>=3.1.0 # Apache-2.0
|
||||||
castellan>=0.16.0 # Apache-2.0
|
castellan>=0.16.0 # Apache-2.0
|
||||||
microversion-parse>=0.2.1 # Apache-2.0
|
microversion-parse>=0.2.1 # Apache-2.0
|
||||||
|
|||||||
Reference in New Issue
Block a user