diff --git a/nova/tests/unit/virt/libvirt/test_driver.py b/nova/tests/unit/virt/libvirt/test_driver.py
index 5ddcd693d854..b1f79397779a 100644
--- a/nova/tests/unit/virt/libvirt/test_driver.py
+++ b/nova/tests/unit/virt/libvirt/test_driver.py
@@ -22918,11 +22918,21 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin):
@mock.patch.object(libvirt_driver.LibvirtDriver, '_get_cpu_feature_traits',
new=mock.Mock(return_value={}))
- def test_cpu_traits_sev_no_feature_traits(self):
+ def test_cpu_traits__sev_support(self):
for support in (False, True):
self.drvr._host._supports_amd_sev = support
- self.assertEqual({ot.HW_CPU_X86_AMD_SEV: support},
- self.drvr._get_cpu_traits())
+ traits = self.drvr._get_cpu_traits()
+ self.assertIn(ot.HW_CPU_X86_AMD_SEV, traits)
+ self.assertEqual(support, traits[ot.HW_CPU_X86_AMD_SEV])
+
+ @mock.patch.object(libvirt_driver.LibvirtDriver, '_get_cpu_feature_traits',
+ new=mock.Mock(return_value={}))
+ def test_cpu_traits__hyperthreading_support(self):
+ for support in (False, True):
+ self.drvr._host._has_hyperthreading = support
+ traits = self.drvr._get_cpu_traits()
+ self.assertIn(ot.HW_CPU_HYPERTHREADING, traits)
+ self.assertEqual(support, traits[ot.HW_CPU_HYPERTHREADING])
def test_cpu_traits_with_passthrough_mode(self):
"""Test getting CPU traits when cpu_mmode is 'host-passthrough', traits
diff --git a/nova/tests/unit/virt/libvirt/test_host.py b/nova/tests/unit/virt/libvirt/test_host.py
index 8ec7af1c99a1..a0e78830b68d 100644
--- a/nova/tests/unit/virt/libvirt/test_host.py
+++ b/nova/tests/unit/virt/libvirt/test_host.py
@@ -1166,6 +1166,56 @@ cg /cgroup/memory cg opt1,opt2 0 0
def test_is_cpu_control_policy_capable_ioerror(self, mock_open):
self.assertFalse(self.host.is_cpu_control_policy_capable())
+ @mock.patch('nova.virt.libvirt.host.libvirt.Connection.getCapabilities')
+ def test_has_hyperthreading__true(self, mock_cap):
+ mock_cap.return_value = """
+
+
+ 1f71d34a-7c89-45cf-95ce-3df20fc6b936
+
+
+
+
+
+
+
+
+
+
+
+ |
+
+
+
+
+ """
+ self.assertTrue(self.host.has_hyperthreading)
+
+ @mock.patch('nova.virt.libvirt.host.libvirt.Connection.getCapabilities')
+ def test_has_hyperthreading__false(self, mock_cap):
+ mock_cap.return_value = """
+
+
+ 1f71d34a-7c89-45cf-95ce-3df20fc6b936
+
+
+
+
+
+
+
+
+
+
+
+ |
+
+
+
+
+ """
+ self.assertFalse(self.host.has_hyperthreading)
+
vc = fakelibvirt.virConnect
diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py
index ed31c5c024ad..dc8d7f127d6e 100644
--- a/nova/virt/libvirt/driver.py
+++ b/nova/virt/libvirt/driver.py
@@ -357,8 +357,8 @@ class LibvirtDriver(driver.ComputeDriver):
self._initiator = None
self._fc_wwnns = None
self._fc_wwpns = None
- self._caps = None
self._supported_perf_events = []
+ self.__has_hyperthreading = None
self.firewall_driver = firewall.load_driver(
DEFAULT_FIREWALL_DRIVER,
host=self._host)
@@ -10269,6 +10269,8 @@ class LibvirtDriver(driver.ComputeDriver):
"""
traits = self._get_cpu_feature_traits()
traits[ot.HW_CPU_X86_AMD_SEV] = self._host.supports_amd_sev
+ traits[ot.HW_CPU_HYPERTHREADING] = self._host.has_hyperthreading
+
return traits
def _get_cpu_feature_traits(self):
diff --git a/nova/virt/libvirt/host.py b/nova/virt/libvirt/host.py
index a65da0ca9ef2..71d2d3e6d498 100644
--- a/nova/virt/libvirt/host.py
+++ b/nova/virt/libvirt/host.py
@@ -119,6 +119,8 @@ class Host(object):
# memoized by the supports_amd_sev property below.
self._supports_amd_sev = None
+ self._has_hyperthreading = None
+
@staticmethod
def _get_libvirt_proxy_classes(libvirt_module):
"""Return a tuple for tpool.Proxy's autowrap argument containing all
@@ -1188,6 +1190,26 @@ class Host(object):
except IOError:
return False
+ @property
+ def has_hyperthreading(self):
+ """Determine if host CPU has SMT, a.k.a. HyperThreading.
+
+ :return: True if the host has SMT enabled, else False.
+ """
+ if self._has_hyperthreading is not None:
+ return self._has_hyperthreading
+
+ self._has_hyperthreading = False
+
+ # we don't use '/capabilities/host/cpu/topology' since libvirt doesn't
+ # guarantee the accuracy of this information
+ for cell in self.get_capabilities().host.topology.cells:
+ if any(len(cpu.siblings) > 1 for cpu in cell.cpus if cpu.siblings):
+ self._has_hyperthreading = True
+ break
+
+ return self._has_hyperthreading
+
def _kernel_supports_amd_sev(self):
if not os.path.exists(SEV_KERNEL_PARAM_FILE):
LOG.debug("%s does not exist", SEV_KERNEL_PARAM_FILE)