libvirt: Add version check when pinning guest CPUs

Ensure versions of libvirt with broken CPU pinning support are not used
for said feature. This requires the addition of a new Exception,
specific version check functionality and unit tests for same.

Change-Id: I03b462c4985517ff8a4d94f0e1acae4fabdc5d39
Closes-Bug: #1438226
This commit is contained in:
Stephen Finucane 2015-03-31 11:03:48 +01:00
parent bf70df295b
commit c380987aa8
4 changed files with 77 additions and 6 deletions

View File

@ -1845,6 +1845,11 @@ class MemoryPageSizeNotSupported(Invalid):
msg_fmt = _("Page size %(pagesize)s is not supported by the host.")
class CPUPinningNotSupported(Invalid):
msg_fmt = _("CPU pinning is not supported by the host: "
"%(reason)s")
class CPUPinningInvalid(Invalid):
msg_fmt = _("Cannot pin/unpin cpus %(requested)s from the following "
"pinned set %(pinned)s")

View File

@ -304,6 +304,50 @@ class HostTestCase(test.NoDBTestCase):
self.assertEqual(self.connect_calls, 1)
self.assertEqual(self.register_calls, 1)
@mock.patch.object(fakelibvirt.virConnect, "getLibVersion")
@mock.patch.object(fakelibvirt.virConnect, "getVersion")
@mock.patch.object(fakelibvirt.virConnect, "getType")
def test_has_min_version(self, fake_hv_type, fake_hv_ver, fake_lv_ver):
fake_lv_ver.return_value = 1002003
fake_hv_ver.return_value = 4005006
fake_hv_type.return_value = 'xyz'
lv_ver = (1, 2, 3)
hv_ver = (4, 5, 6)
hv_type = 'xyz'
self.assertTrue(self.host.has_min_version(lv_ver, hv_ver, hv_type))
self.assertFalse(self.host.has_min_version(lv_ver, hv_ver, 'abc'))
self.assertFalse(self.host.has_min_version(lv_ver, (4, 5, 7), hv_type))
self.assertFalse(self.host.has_min_version((1, 3, 3), hv_ver, hv_type))
self.assertTrue(self.host.has_min_version(lv_ver, hv_ver, None))
self.assertTrue(self.host.has_min_version(lv_ver, None, hv_type))
self.assertTrue(self.host.has_min_version(None, hv_ver, hv_type))
@mock.patch.object(fakelibvirt.virConnect, "getLibVersion")
@mock.patch.object(fakelibvirt.virConnect, "getVersion")
@mock.patch.object(fakelibvirt.virConnect, "getType")
def test_has_version(self, fake_hv_type, fake_hv_ver, fake_lv_ver):
fake_lv_ver.return_value = 1002003
fake_hv_ver.return_value = 4005006
fake_hv_type.return_value = 'xyz'
lv_ver = (1, 2, 3)
hv_ver = (4, 5, 6)
hv_type = 'xyz'
self.assertTrue(self.host.has_version(lv_ver, hv_ver, hv_type))
for lv_ver_ in [(1, 2, 2), (1, 2, 4)]:
self.assertFalse(self.host.has_version(lv_ver_, hv_ver, hv_type))
for hv_ver_ in [(4, 4, 6), (4, 6, 6)]:
self.assertFalse(self.host.has_version(lv_ver, hv_ver_, hv_type))
self.assertFalse(self.host.has_version(lv_ver, hv_ver, 'abc'))
self.assertTrue(self.host.has_min_version(lv_ver, hv_ver, None))
self.assertTrue(self.host.has_min_version(lv_ver, None, hv_type))
self.assertTrue(self.host.has_min_version(None, hv_ver, hv_type))
@mock.patch.object(fakelibvirt.virConnect, "lookupByID")
def test_get_domain_by_id(self, fake_lookup):
dom = fakelibvirt.virDomain(self.host.get_connection(),

View File

@ -363,6 +363,8 @@ MIN_LIBVIRT_NUMA_VERSION = (1, 2, 7)
# cannot make guaranteed decisions, as the huge page size
# used by the guest may not match what was requested
MIN_LIBVIRT_HUGEPAGE_VERSION = (1, 2, 8)
# missing libvirt cpu pinning support
BAD_LIBVIRT_CPU_POLICY_VERSIONS = [(1, 2, 9, 2), (1, 2, 10)]
# fsFreeze/fsThaw requirement
MIN_LIBVIRT_FSFREEZE_VERSION = (1, 2, 5)
@ -519,7 +521,7 @@ class LibvirtDriver(driver.ComputeDriver):
self._set_host_enabled(enabled, reason)
def _version_to_string(self, version):
return "%i.%i.%i" % version
return '.'.join([str(x) for x in version])
def init_host(self, host):
self._host.initialize()
@ -3396,6 +3398,14 @@ class LibvirtDriver(driver.ComputeDriver):
guest_cpu_numa.cells.append(guest_cell)
return guest_cpu_numa
def _has_cpu_policy_support(self):
for ver in BAD_LIBVIRT_CPU_POLICY_VERSIONS:
if self._host.has_version(ver):
ver_ = self._version_to_string(version)
raise exception.CPUPinningNotSupported(reason=_(
'Invalid libvirt version %(version)s') % {'version': ver_})
return True
def _get_guest_numa_config(self, instance_numa_topology, flavor, pci_devs,
allowed_cpus=None):
"""Returns the config objects for the guest NUMA specs.
@ -3499,7 +3509,9 @@ class LibvirtDriver(driver.ComputeDriver):
# If there is pinning information in the cell
# we pin to individual CPUs, otherwise we float
# over the whole host NUMA node
if object_numa_cell.cpu_pinning:
if (object_numa_cell.cpu_pinning and
self._has_cpu_policy_support()):
pcpu = object_numa_cell.cpu_pinning[cpu]
pin_cpuset.cpuset = set([pcpu])
else:

View File

@ -27,6 +27,7 @@ the raw libvirt API. These APIs are then used by all
the other libvirt related classes
"""
import operator
import os
import socket
import threading
@ -546,18 +547,19 @@ class Host(object):
libvirt.virEventRegisterDefaultImpl()
self._init_events()
def has_min_version(self, lv_ver=None, hv_ver=None, hv_type=None):
def _version_check(self, lv_ver=None, hv_ver=None, hv_type=None,
op=operator.lt):
conn = self.get_connection()
try:
if lv_ver is not None:
libvirt_version = conn.getLibVersion()
if libvirt_version < utils.convert_version_to_int(lv_ver):
if op(libvirt_version, utils.convert_version_to_int(lv_ver)):
return False
if hv_ver is not None:
hypervisor_version = conn.getVersion()
if hypervisor_version < utils.convert_version_to_int(hv_ver):
if op(hypervisor_version,
utils.convert_version_to_int(hv_ver)):
return False
if hv_type is not None:
@ -569,6 +571,14 @@ class Host(object):
except Exception:
return False
def has_min_version(self, lv_ver=None, hv_ver=None, hv_type=None):
return self._version_check(
lv_ver=lv_ver, hv_ver=hv_ver, hv_type=hv_type, op=operator.lt)
def has_version(self, lv_ver=None, hv_ver=None, hv_type=None):
return self._version_check(
lv_ver=lv_ver, hv_ver=hv_ver, hv_type=hv_type, op=operator.ne)
def get_domain(self, instance):
"""Retrieve libvirt domain object for an instance.