libvirt: Add Intel TDX host capability detection

Adds host capability detection for Intel TDX to the libvirt driver.
Detection is performed using 2 mechanisms: libvirt domain capabilities
and the capacity exposed by the cgroup misc controller
(/sys/fs/cgroup/misc.capacity). The latter is used to report the number
of available TDX keys. Intel TDX also requires CPU mode to be
'host-passthrough'.

The config option [libvirt] 'num_intel_tdx_guests' can also be used to
configure the maximum number of available TDX keys. This is to
accomondate deployments where cgroup misc controller does not reflect
the information by the host.

Bumps os-traits dependency to version 3.8.0 to use Intel TDX trait.
Bumps ImageMetaProps version to 1.44.
Bumps ImageMetaPropsPayload version to 1.20.

Partial-Implements: blueprint intel-tdx-libvirt-support
Change-Id: I67315422eccc113390fedc905d3217a2d73367a1
Signed-off-by: antia <anton.iacobaeus@canarybit.eu>
This commit is contained in:
antia committed 2026-08-21 07:48:20 -07:00
1 parent 07714e5030
commit a69768e478
14 files changed
+403 -10

No files matched your search

@@ -4,5 +4,5 @@
"hw_architecture": "x86_64"
},
"nova_object.name": "ImageMetaPropsPayload",
"nova_object.version": "1.19"
"nova_object.version": "1.20"
}
+21
View File
@@ -946,6 +946,27 @@ Related options:
* ``my_ip``
* ``live_migration_inbound_addr``
"""),
cfg.IntOpt('num_intel_tdx_guests',
default=None,
min=0,
help="""
Maximum number of guests with Intel TDX (Intel Trust Domain Extensions) memory
encryption that this compute host can run concurrently.
Intel TDX capacity is normally determined automatically by reading the number
of available keys exposed by the kernel's misc cgroup controller (``tdx``
entry in ``misc.capacity``), and this option does not need to be set in that
case.
However cgroup detection is not always possible, for example when nova-compute
is deployed inside a container without visibility of the host's cgroup
hierarchy. If the cgroup value cannot be read, this option becomes the only
source of the limit and **must** be set, or Nova will assume the host does not
support Intel TDX.
If the cgroup value *can* be read and this option is also set, the lower
of the two values is used.
"""),
]
+2 -1
View File
@@ -137,7 +137,8 @@ class ImageMetaPropsPayload(base.NotificationPayloadBase):
# Version 1.17: Added 'hw_usb_model' and 'hw_redirected_usb_ports' fields
# Version 1.18: Added 'hw_mem_encryption_model' field
# Version 1.19: Added 'amd-sev-snp' as an option to hw_mem_encryption_model
VERSION = '1.19'
# Version 1.20: Added 'intel-tdx' as an option to hw_mem_encryption_model
VERSION = '1.20'
# NOTE(efried): This logic currently relies on all of the fields of
# ImageMetaProps being initialized with no arguments. See the docstring.
+2 -1
View File
@@ -565,8 +565,9 @@ class MemEncryptionModel(BaseNovaEnum):
AMD_SEV = "amd-sev"
AMD_SEV_ES = "amd-sev-es"
AMD_SEV_SNP = "amd-sev-snp"
INTEL_TDX = "intel-tdx"
ALL = (AMD_SEV, AMD_SEV_ES, AMD_SEV_SNP)
ALL = (AMD_SEV, AMD_SEV_ES, AMD_SEV_SNP, INTEL_TDX)
class MaxPhyAddrMode(BaseNovaEnum):
+7 -1
View File
@@ -207,15 +207,21 @@ class ImageMetaProps(base.NovaObject):
# Version 1.41: Added 'hw_usb_model' and 'hw_redirected_usb_ports' fields
# Version 1.42: Added 'hw_mem_encryption_model' field
# Version 1.43: Added 'amd-sev-snp' as an option to hw_mem_encryption_model
# Version 1.44: Added 'intel-tdx' as an option to hw_mem_encryption_model
# NOTE(efried): When bumping this version, the version of
# ImageMetaPropsPayload must also be bumped. See its docstring for details.
VERSION = '1.43'
VERSION = '1.44'
def obj_make_compatible(self, primitive, target_version): # noqa: C901
super(ImageMetaProps, self).obj_make_compatible(primitive,
target_version)
target_version = versionutils.convert_version_to_tuple(target_version)
if target_version < (1, 44):
base.raise_on_too_new_values(
target_version, primitive,
'hw_mem_encryption_model',
(fields.MemEncryptionModel.INTEL_TDX,))
if target_version < (1, 43):
base.raise_on_too_new_values(
target_version, primitive,
+12
View File
@@ -2475,6 +2475,18 @@ class Connection(object):
</enum>
</launchSecurity>'''
_domain_capability_features_with_TDX = '''\
<features>
<gic supported='no'/>
<tdx supported='yes'/>
</features>'''
_domain_capability_features_with_TDX_unsupported = '''\
<features>
<gic supported='no'/>
<tdx supported='no'/>
</features>'''
def getCapabilities(self):
"""Return spoofed capabilities."""
numa_topology = self.host_info.numa_topology
@@ -1258,7 +1258,7 @@ class TestInstanceNotificationSample(
'nova_object.data': {},
'nova_object.name': 'ImageMetaPropsPayload',
'nova_object.namespace': 'nova',
'nova_object.version': '1.19',
'nova_object.version': '1.20',
},
'image.size': 58145823,
'image.tags': [],
@@ -1354,7 +1354,7 @@ class TestInstanceNotificationSample(
'nova_object.data': {},
'nova_object.name': 'ImageMetaPropsPayload',
'nova_object.namespace': 'nova',
'nova_object.version': '1.19',
'nova_object.version': '1.20',
},
'image.size': 58145823,
'image.tags': [],
@@ -385,7 +385,7 @@ notification_object_data = {
# ImageMetaProps, so when you see a fail here for that reason, you must
# *also* bump the version of ImageMetaPropsPayload. See its docstring for
# more information.
'ImageMetaPropsPayload': '1.19-473614bfe8a179dbe4f95334193334c3',
'ImageMetaPropsPayload': '1.20-daa011f0b11624db935738ee0316ef78',
'InstanceActionNotification': '1.0-a73147b93b520ff0061865849d3dfa56',
'InstanceActionPayload': '1.9-525dcf81b6e4592d935712a2675309dc',
'InstanceActionRebuildNotification':
+1 -1
View File
@@ -1105,7 +1105,7 @@ object_data = {
'HyperVLiveMigrateData': '1.5-b424b27305f259fb3c15d720856585c7',
'IDEDeviceBus': '1.0-29d4c9f27ac44197f01b6ac1b7e16502',
'ImageMeta': '1.8-642d1b2eb3e880a367f37d72dd76162d',
'ImageMetaProps': '1.43-4a5ae3aad555458103fe5dbee297341d',
'ImageMetaProps': '1.44-fd4f63a7cfab5a0ea0e034c40a554b26',
'Instance': '2.8-2727dba5e4a078e6cc848c1f94f7eb24',
'InstanceAction': '1.2-9a5abc87fdd3af46f45731960651efb5',
'InstanceActionEvent': '1.4-5b1f361bd81989f8bb2c20bb7e8a4cb4',
@@ -3318,6 +3318,10 @@ class LibvirtConnTestCase(test.NoDBTestCase,
@mock.patch.object(host.Host, "_check_machine_type", new=mock.Mock())
@mock.patch('nova.virt.libvirt.utils.get_default_machine_type',
new=mock.Mock(return_value='config-machine_type'))
# the getDomainCapabilities test stub is called by memory encryption config
# but it doesn't support the faked machine type used in this test
@mock.patch.object(libvirt_driver.LibvirtDriver,
'_get_mem_encryption_config', new=mock.Mock(return_value=None))
def test_get_guest_config_records_machine_type_in_instance(self):
# Assert that the config derived machine type is used when it
# isn't present in the image_meta of an instance.
@@ -6041,6 +6045,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
def test_get_guest_config_with_uefi(self):
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
drvr._host._supports_amd_sev = False
drvr._host._supports_intel_tdx = False
drvr._host._supports_uefi = True
image_meta = objects.ImageMeta.from_dict({
@@ -6066,6 +6071,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
def test_get_guest_config_with_uefi_old_guest(self, mock_exists):
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
drvr._host._supports_amd_sev = False
drvr._host._supports_intel_tdx = False
drvr._host._supports_uefi = True
image_meta = objects.ImageMeta.from_dict({
@@ -6106,6 +6112,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
self, mock_exists):
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
drvr._host._supports_amd_sev = False
drvr._host._supports_intel_tdx = False
drvr._host._supports_uefi = True
image_meta = objects.ImageMeta.from_dict({
@@ -6149,6 +6156,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
self, mock_exists):
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
drvr._host._supports_amd_sev = False
drvr._host._supports_intel_tdx = False
drvr._host._supports_uefi = True
image_meta = objects.ImageMeta.from_dict({
@@ -6190,6 +6198,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
def test_get_guest_config_with_uefi_and_stateless_firmware(self):
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
drvr._host._supports_amd_sev = False
drvr._host._supports_intel_tdx = False
drvr._host._supports_uefi = True
image_meta = objects.ImageMeta.from_dict({
@@ -6221,6 +6230,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
):
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
drvr._host._supports_amd_sev = False
drvr._host._supports_intel_tdx = False
drvr._host._supports_uefi = True
drvr._host._supports_secure_boot = host_has_support
@@ -6261,6 +6271,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
):
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
drvr._host._supports_amd_sev = False
drvr._host._supports_intel_tdx = False
drvr._host._supports_uefi = True
drvr._host._supports_secure_boot = host_has_support
@@ -6298,6 +6309,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
self, mock_exists, smm):
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
drvr._host._supports_amd_sev = False
drvr._host._supports_intel_tdx = False
drvr._host._supports_uefi = True
drvr._host._supports_secure_boot = True
@@ -6349,6 +6361,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
self, mock_exists):
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
drvr._host._supports_amd_sev = False
drvr._host._supports_intel_tdx = False
drvr._host._supports_uefi = True
drvr._host._supports_secure_boot = False
@@ -32569,6 +32582,9 @@ class TestLibvirtSEVUnsupported(TestLibvirtSEV):
},
'amd_sev_snp': {
'total': 0
},
'intel_tdx': {
'total': 0
}
}, self.driver._get_memory_encryption_inventories())
@@ -32605,6 +32621,9 @@ class TestLibvirtSEVSupported(TestLibvirtSEV):
'amd_sev_snp': {
'total': 0
},
'intel_tdx': {
'total': 0
}
}, self.driver._get_memory_encryption_inventories())
+180
View File
@@ -2555,6 +2555,186 @@ class TestLibvirtSEVSNPSupported(TestLibvirtSEV):
inventories['amd_sev_snp'])
class TestLibvirtTDX(test.NoDBTestCase):
"""Libvirt host tests for Intel TDX support."""
def setUp(self):
super(TestLibvirtTDX, self).setUp()
self.useFixture(nova_fixtures.LibvirtFixture())
self.flags(cpu_mode='host-passthrough', group='libvirt')
self.host = host.Host("qemu:///system")
self.addCleanup(self.host.cleanup)
self.host._delayed_executor = (
thread_pool_factory.
StaticallyDelayingCancellableTaskExecutorWrapper(
delay=0.1, executor=thread_pool_factory.get_executor(
thread_pool_factory.ExecutorType.DEFAULT)))
self.misc_capacity = None
def _exists(path):
if path == '/sys/fs/cgroup/misc.capacity':
return self.misc_capacity is not None
elif path == '/sys/module/kvm_amd/parameters/sev':
# Mock as absent so supports_mem_encryption() reports no SEV
# support in TDX scenarios
return None
self.fail('Unexpected path is accessed by os.path: %s' % path)
self.mock_exists = self.useFixture(fixtures.MockPatch(
'os.path.exists', side_effect=_exists)).mock
def _open(path):
if path == '/sys/fs/cgroup/misc.capacity':
return io.StringIO(self.misc_capacity)
self.fail('Unexpected path is accessed by open: %s' % path)
self.mock_open = self.useFixture(fixtures.MockPatch(
'builtins.open', side_effect=_open)).mock
class TestLibvirtTDXUnsupported(TestLibvirtTDX):
def test_unsupported_without_feature(self):
self.assertFalse(self.host.supports_intel_tdx)
self.assertFalse(self.host.supports_mem_encryption)
@mock.patch.object(vc, '_domain_capability_features',
new=vc._domain_capability_features_with_TDX)
def test_unsupported_without_host_passthrough(self):
self.flags(cpu_mode='host-model', group='libvirt')
self.assertFalse(self.host.supports_intel_tdx)
self.assertFalse(self.host.supports_mem_encryption)
@mock.patch.object(vc, '_domain_capability_features',
new=vc._domain_capability_features_with_TDX_unsupported)
def test_unsupported_with_feature(self):
self.assertFalse(self.host.supports_intel_tdx)
self.assertFalse(self.host.supports_mem_encryption)
def test_non_x86_architecture(self):
fake_caps_xml = '''
<capabilities>
<host>
<uuid>cef19ce0-0ca2-11df-855d-b19fbce37686</uuid>
<cpu>
<arch>aarch64</arch>
</cpu>
</host>
</capabilities>'''
with mock.patch.object(fakelibvirt.virConnect, 'getCapabilities',
return_value=fake_caps_xml):
self.assertFalse(self.host.supports_intel_tdx)
self.assertFalse(self.host.supports_mem_encryption)
def test_slots(self):
self.assertEqual(0, self.host._get_mem_encryption_slots_intel_tdx())
def test_slots_with_config_set(self):
self.flags(num_intel_tdx_guests=63, group='libvirt')
self.assertEqual(0, self.host._get_mem_encryption_slots_intel_tdx())
def test_inventory_traits(self):
self.assertEqual(['HW_CPU_X86_INTEL_TDX'],
self.host._get_mem_encryption_traits_intel_tdx())
def test_inventory(self):
inventories = self.host.get_mem_encryption_inventories()
self.assertIn('intel_tdx', inventories)
self.assertEqual({'supported': False}, inventories['intel_tdx'])
class TestLibvirtTDXSupported(TestLibvirtTDX):
"""Libvirt driver tests for when Intel TDX support is present."""
@mock.patch.object(vc, '_domain_capability_features',
new=vc._domain_capability_features_with_TDX)
def test_supported_with_feature(self):
self.misc_capacity = "tdx 0\n"
self.assertTrue(self.host.supports_intel_tdx)
self.assertTrue(self.host.supports_mem_encryption)
@mock.patch.object(vc, '_domain_capability_features',
new=vc._domain_capability_features_with_TDX)
def test_slots_with_capacity_and_config_absent(self):
self.misc_capacity = "tdx 63\n"
self.assertTrue(self.host.supports_intel_tdx)
self.assertTrue(self.host.supports_mem_encryption)
self.assertEqual(63, self.host._get_mem_encryption_slots_intel_tdx())
@mock.patch.object(vc, '_domain_capability_features',
new=vc._domain_capability_features_with_TDX)
def test_slots_with_capacity_and_config_less(self):
self.misc_capacity = "tdx 63\n"
self.flags(num_intel_tdx_guests=1, group='libvirt')
self.assertTrue(self.host.supports_intel_tdx)
self.assertTrue(self.host.supports_mem_encryption)
self.assertEqual(1, self.host._get_mem_encryption_slots_intel_tdx())
@mock.patch.object(vc, '_domain_capability_features',
new=vc._domain_capability_features_with_TDX)
def test_slots_with_capacity_and_config_equal(self):
self.misc_capacity = "tdx 63\n"
self.flags(num_intel_tdx_guests=63, group='libvirt')
self.assertTrue(self.host.supports_intel_tdx)
self.assertTrue(self.host.supports_mem_encryption)
self.assertEqual(63, self.host._get_mem_encryption_slots_intel_tdx())
@mock.patch.object(vc, '_domain_capability_features',
new=vc._domain_capability_features_with_TDX)
def test_slots_with_capacity_and_config_more(self):
self.misc_capacity = "tdx 63\n"
self.flags(num_intel_tdx_guests=100, group='libvirt')
self.assertTrue(self.host.supports_intel_tdx)
self.assertTrue(self.host.supports_mem_encryption)
self.assertEqual(100, self.host._get_mem_encryption_slots_intel_tdx())
@mock.patch.object(vc, '_domain_capability_features',
new=vc._domain_capability_features_with_TDX)
def test_slots_capacity_file_and_config_absent_when_supported(self):
self.assertTrue(self.host.supports_intel_tdx)
self.assertTrue(self.host.supports_mem_encryption)
self.assertEqual(0, self.host._get_mem_encryption_slots_intel_tdx())
@mock.patch.object(vc, '_domain_capability_features',
new=vc._domain_capability_features_with_TDX)
def test_slots_capacity_file_absent_and_config_zero_when_supported(self):
self.flags(num_intel_tdx_guests=0, group='libvirt')
self.assertTrue(self.host.supports_intel_tdx)
self.assertTrue(self.host.supports_mem_encryption)
self.assertEqual(0, self.host._get_mem_encryption_slots_intel_tdx())
@mock.patch.object(vc, '_domain_capability_features',
new=vc._domain_capability_features_with_TDX)
def test_slots_capacity_file_absent_and_config_set_when_supported(self):
self.flags(num_intel_tdx_guests=63, group='libvirt')
self.assertTrue(self.host.supports_intel_tdx)
self.assertTrue(self.host.supports_mem_encryption)
self.assertEqual(63, self.host._get_mem_encryption_slots_intel_tdx())
@mock.patch.object(vc, '_domain_capability_features',
new=vc._domain_capability_features_with_TDX)
def test_inventory_traits(self):
self.assertEqual(['HW_CPU_X86_INTEL_TDX'],
self.host._get_mem_encryption_traits_intel_tdx())
@mock.patch.object(vc, '_domain_capability_features',
new=vc._domain_capability_features_with_TDX)
def test_inventory_supported(self):
self.misc_capacity = "tdx 100\n"
self.assertTrue(self.host.supports_intel_tdx)
self.assertTrue(self.host.supports_mem_encryption)
inventories = self.host.get_mem_encryption_inventories()
self.assertIn('intel_tdx', inventories)
self.assertEqual(
{
'supported': True,
'total': 100,
'traits': ['HW_CPU_X86_INTEL_TDX'],
},
inventories['intel_tdx'])
class LibvirtTpoolProxyTestCase(test.NoDBTestCase):
def setUp(self):
if utils.concurrency_mode_threading():
+15
View File
@@ -337,6 +337,8 @@ class LibvirtConfigDomainCapsFeatures(LibvirtConfigObject):
feature = None
if c.tag == "sev":
feature = LibvirtConfigDomainCapsFeatureSev()
if c.tag == "tdx":
feature = LibvirtConfigDomainCapsFeatureTDX()
if c.tag == "launchSecurity":
feature = LibvirtConfigDomainCapsFeatureLaunchSecurity()
if feature:
@@ -397,6 +399,19 @@ class LibvirtConfigDomainCapsFeatureLaunchSecurity(LibvirtConfigObject):
child.text for child in c if child.tag == 'value']
class LibvirtConfigDomainCapsFeatureTDX(LibvirtConfigObject):
def __init__(self, **kwargs):
super().__init__(root_name='tdx', **kwargs)
self.supported = False
def parse_dom(self, xmldoc):
super().parse_dom(xmldoc)
if xmldoc.get('supported') == 'yes':
self.supported = True
class LibvirtConfigDomainCapsOS(LibvirtConfigObject):
def __init__(self, **kwargs):
+139 -1
View File
@@ -82,6 +82,8 @@ SEV_KERNEL_PARAM_FILE = '/sys/module/kvm_amd/parameters/%s'
MIN_QEMU_SEV_ES_VERSION = (8, 0, 0)
MISC_CONTROL_GROUP_CAPACITY_FILE = '/sys/fs/cgroup/misc.capacity'
class LibvirtEventHandler:
def __init__(self, conn_event_handler=None, lifecycle_event_handler=None):
@@ -328,6 +330,8 @@ class Host(object):
self._supports_amd_sev_snp: bool | None = None
self._max_sev_guests: int = 0
self._max_sev_es_guests: int = 0
self._supports_intel_tdx: bool | None = None
self._max_intel_tdx_guests: int | None = None
self._supports_uefi: bool | None = None
self._supports_secure_boot: bool | None = None
@@ -2151,6 +2155,134 @@ class Host(object):
self._supports_amd_sev_snp = False
return self._supports_amd_sev_snp
@property
def supports_intel_tdx(self) -> bool:
"""Determine if the host supports Intel TDX for guests.
Returns a boolean indicating whether Intel Trust Domain Extensions
(Intel TDX) is supported. This is conditional on support in the
hardware, kernel, qemu, and libvirt.
CPU mode also has to be configured to host-passthrough, since Intel TDX
enabled guests require it.
This checks whether the feature is supported by *any* machine type.
This is only used for trait-reporting purposes and a machine
type-specific check should be used when creating guests.
"""
if self._supports_intel_tdx is not None:
return self._supports_intel_tdx
self._supports_intel_tdx = False
caps = self.get_capabilities()
if caps.host.cpu.arch != fields.Architecture.X86_64:
return self._supports_intel_tdx
# NOTE(antia): This effectively requires cpu_mode to be explicitly
# configured to "host-passthrough" to enable Intel TDX support. TDX
# requires the guest to see the real host CPU, so host-passthrough is a
# hard prerequisite.
cpu_mode = CONF.libvirt.cpu_mode
if cpu_mode != "host-passthrough":
LOG.debug(
"Skipping Intel TDX detection: cpu_mode is not "
"'host-passthrough', which is required.",
)
return self._supports_intel_tdx
domain_caps = self.get_domain_capabilities()
for arch in domain_caps:
for machine_type in domain_caps[arch]:
LOG.debug(
"Checking TDX support for arch %s and machine type %s",
arch,
machine_type,
)
for feature in domain_caps[arch][machine_type].features:
feature_is_tdx = isinstance(
feature, vconfig.LibvirtConfigDomainCapsFeatureTDX
)
if feature_is_tdx and feature.supported:
LOG.info("Intel TDX support detected")
self._supports_intel_tdx = True
return self._supports_intel_tdx
LOG.debug("No Intel TDX support detected for any (arch, machine_type)")
return self._supports_intel_tdx
def _get_cgroup_tdx_capacity(self):
"""Read kernel misc control group for maximum Intel TDX guests.
The misc control group is a generic interface for any resource that may
need limiting, it is not specific to Intel TDX. Only the capacity part
is used.
https://lwn.net/Articles/856438/
"""
capacity_file = MISC_CONTROL_GROUP_CAPACITY_FILE
if not os.path.exists(capacity_file):
LOG.debug("%s does not exist", capacity_file)
return None
with open(capacity_file) as f:
content = f.read()
LOG.debug("%s contains [%s]", capacity_file, content)
for line in content.splitlines():
parts = line.split()
if len(parts) == 2 and parts[0] == 'tdx':
try:
return int(parts[1])
except ValueError:
LOG.warning(
"Failed to parse TDX capacity from %s: "
"unexpected value [%s]", capacity_file, parts[1])
return None
LOG.warning("No TDX entry found in %s", capacity_file)
return None
def _get_tdx_capacity(self) -> int:
"""Determine the maximum number of Intel TDX guests this host can run
concurrently.
The primary source is the kernel misc control group (see
_get_cgroup_tdx_capacity). In case this is unavailable
[libvirt] num_tdx_guests becomes the only source and must be set.
If both are set then the smallest of the two are used.
"""
cgroup_capacity = self._get_cgroup_tdx_capacity()
conf_capacity = CONF.libvirt.num_intel_tdx_guests
if cgroup_capacity is None:
if conf_capacity is None:
LOG.warning(
"Unable to determine Intel TDX capacity from the misc "
"cgroup, and libvirt.num_intel_tdx_guests is not set. "
"Assuming this host does not support Intel TDX.")
return 0
return conf_capacity
if conf_capacity is not None:
if conf_capacity > cgroup_capacity:
LOG.warning(
"Host is configured with libvirt.num_intel_tdx_guests "
"set to %d, but only supports %d Intel TDX guests "
"according to the misc cgroup.",
conf_capacity, cgroup_capacity)
return conf_capacity
return cgroup_capacity
@property
def max_intel_tdx_guests(self) -> int:
"""Determine maximum number of guests with Intel TDX.
"""
if not self.supports_intel_tdx:
return 0
if self._max_intel_tdx_guests is None:
self._max_intel_tdx_guests = self._get_tdx_capacity()
return self._max_intel_tdx_guests
@property
def supports_mem_encryption(self) -> bool:
"""Determine if the host supports memory encryption for guests.
@@ -2163,7 +2295,7 @@ class Host(object):
is supported.
"""
return self.supports_amd_sev
return self.supports_amd_sev or self.supports_intel_tdx
def get_mem_encryption_inventories(self) -> dict[str, ty.Any]:
"""Return a dictionary of memory encryption information.
@@ -2220,6 +2352,12 @@ class Host(object):
def _get_mem_encryption_traits_amd_sev_snp(self) -> list[str]:
return [ot.HW_CPU_X86_AMD_SEV_SNP]
def _get_mem_encryption_slots_intel_tdx(self) -> int:
return self.max_intel_tdx_guests
def _get_mem_encryption_traits_intel_tdx(self) -> list[str]:
return [ot.HW_CPU_X86_INTEL_TDX]
@property
def supports_remote_managed_ports(self) -> bool:
"""Determine if the host supports remote managed ports.
+1 -1
View File
@@ -50,7 +50,7 @@ psutil>=3.2.2 # BSD
oslo.versionedobjects>=1.35.0 # Apache-2.0
os-brick>=6.10.0 # Apache-2.0
os-resource-classes>=1.1.0 # Apache-2.0
os-traits>=3.7.0 # Apache-2.0
os-traits>=3.8.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