diff --git a/nova/test.py b/nova/test.py
index b483ef469513..8359aafa8bd3 100644
--- a/nova/test.py
+++ b/nova/test.py
@@ -422,6 +422,12 @@ class TestCase(base.BaseTestCase):
for k, v in kw.items():
CONF.set_override(k, v, group)
+ def reset_flags(self, *k, **kw):
+ """Reset flag variables for a test."""
+ group = kw.pop('group')
+ for flag in k:
+ CONF.clear_override(flag, group)
+
def enforce_fk_constraints(self, engine=None):
if engine is None:
engine = db_api.get_engine()
diff --git a/nova/tests/fixtures/libvirt.py b/nova/tests/fixtures/libvirt.py
index 76840ef52c58..5b2689d0105b 100644
--- a/nova/tests/fixtures/libvirt.py
+++ b/nova/tests/fixtures/libvirt.py
@@ -905,6 +905,13 @@ def _parse_disk_info(element):
return disk_info
+def _parse_vcpu_info(element):
+ vcpu_info = {}
+ vcpu_info['number'] = int(element.text)
+ vcpu_info['cpuset'] = element.get('cpuset')
+ return vcpu_info
+
+
def _parse_nic_info(element):
nic_info = {}
nic_info['type'] = element.get('type', 'bridge')
@@ -1097,7 +1104,7 @@ class Domain(object):
vcpu = tree.find('./vcpu')
if vcpu is not None:
- definition['vcpu'] = int(vcpu.text)
+ definition['vcpu'] = _parse_vcpu_info(vcpu)
memory = tree.find('./memory')
if memory is not None:
@@ -1543,12 +1550,16 @@ class Domain(object):
"""
+ vcpuset = ''
+ if self._def['vcpu'].get('cpuset'):
+ vcpuset = ' cpuset="' + self._def['vcpu']['cpuset'] + '"'
+
return '''
%(name)s
%(uuid)s
%(memory)s
%(memory)s
- %(vcpu)s
+ %(vcpu)s
hvm
@@ -1596,7 +1607,8 @@ class Domain(object):
''' % {'name': self._def['name'],
'uuid': self._def['uuid'],
'memory': self._def['memory'],
- 'vcpu': self._def['vcpu'],
+ 'vcpuset': vcpuset,
+ 'vcpu': self._def['vcpu']['number'],
'arch': self._def['os']['arch'],
'disks': disks,
'nics': nics,
@@ -1628,7 +1640,7 @@ class Domain(object):
def vcpus(self):
vcpus = ([], [])
- for i in range(0, self._def['vcpu']):
+ for i in range(0, self._def['vcpu']['number']):
vcpus[0].append((i, 1, 120405, i))
vcpus[1].append((True, True, True, True))
return vcpus
diff --git a/nova/tests/functional/libvirt/test_live_migration.py b/nova/tests/functional/libvirt/test_live_migration.py
index 31ff9dfca05b..c339a79196f7 100644
--- a/nova/tests/functional/libvirt/test_live_migration.py
+++ b/nova/tests/functional/libvirt/test_live_migration.py
@@ -210,3 +210,124 @@ class LiveMigrationQueuedAbortTestLeftoversRemoved(LiveMigrationWithLockBase):
)
self.assertEqual(1, len(port_binding_server_b))
self.assertNotIn('dest', port_binding_server_b)
+
+
+class LiveMigrationWithCpuSharedSet(
+ libvirt_base.LibvirtMigrationMixin,
+ libvirt_base.ServersTestBase,
+ integrated_helpers.InstanceHelperMixin
+):
+
+ api_major_version = 'v2.1'
+ # Microversion 2.74 is required to boot a server on a specific host,
+ # which is used in the below tests.
+ microversion = '2.74'
+ ADMIN_API = True
+
+ def setUp(self):
+ super().setUp()
+
+ self.src_hostname = self.start_compute(hostname='src')
+ self.dest_hostname = self.start_compute(hostname='dest')
+
+ self.src = self.computes[self.src_hostname]
+ self.dest = self.computes[self.dest_hostname]
+
+ def get_host(self, server_id):
+ server = self.api.get_server(server_id)
+ return server['OS-EXT-SRV-ATTR:host']
+
+ def test_live_migration_to_different_cpu_shared_set(self):
+ """Reproducer for bug 1869804 #1.
+ An instance live migrated from a host with a cpu_shared_set to a
+ destination host with a different cpu_shared_set should be updated
+ to use the destination cpu_shared_set.
+ """
+ self.flags(cpu_shared_set='0,1', group='compute')
+ self.restart_compute_service('src')
+ self.restart_compute_service('dest')
+ self.server = self._create_server(host='src', networks='none')
+
+ conn = self.src.driver._host.get_connection()
+ dom = conn.lookupByUUIDString(self.server['id'])
+ xml = dom.XMLDesc(0)
+ self.assertIn('1', xml)
+
+ self.flags(cpu_shared_set='3,4', group='compute')
+ self.restart_compute_service('dest')
+ self._live_migrate(self.server, 'completed')
+ self.assertEqual('dest', self.get_host(self.server['id']))
+
+ conn = self.dest.driver._host.get_connection()
+ dom = conn.lookupByUUIDString(self.server['id'])
+ xml = dom.XMLDesc(0)
+ # The destination should be updated to "3-4" but it is not the case.
+ self.assertIn('1', xml)
+ self.assertNotIn('1', xml)
+
+ def test_live_migration_to_no_cpu_shared_set(self):
+ """Reproducer for bug 1869804 #2.
+ An instance live migrated from a host with a cpu_shared_set to a
+ destination host without cpu_shared_set should not keep cpuset
+ settings.
+ """
+ self.flags(cpu_shared_set='0,1', group='compute')
+ self.restart_compute_service('src')
+ self.restart_compute_service('dest')
+ self.server = self._create_server(host='src', networks='none')
+
+ self.reset_flags('cpu_shared_set', group='compute')
+ self.restart_compute_service('src')
+ self.restart_compute_service('dest')
+
+ # Here we just create a server2 to ensure cpu_shared_set is not
+ # configured on destination host.
+ self.server2 = self._create_server(host='dest', networks='none')
+
+ conn = self.src.driver._host.get_connection()
+ dom = conn.lookupByUUIDString(self.server['id'])
+ xml = dom.XMLDesc(0)
+ self.assertIn('1', xml)
+
+ conn = self.dest.driver._host.get_connection()
+ dom = conn.lookupByUUIDString(self.server2['id'])
+ xml = dom.XMLDesc(0)
+ # This prove that cpu_shared_set is not configured on destination host
+ self.assertIn('1', xml)
+
+ self._live_migrate(self.server, 'completed')
+ self.assertEqual('dest', self.get_host(self.server['id']))
+
+ conn = self.dest.driver._host.get_connection()
+ dom = conn.lookupByUUIDString(self.server['id'])
+ xml = dom.XMLDesc(0)
+ # The destination cpuset should be removed because the
+ # host has no cpu_shared_set configured. Which is not the case due to
+ # the bug.
+ self.assertIn('1', xml)
+ self.assertNotIn('1', xml)
+
+ def test_live_migration_from_no_cpu_shared_set_to_cpu_shared_set(self):
+ """Reproducer for bug 1869804 #3.
+ An instance live migrated from a host without a cpu_shared_set to a
+ destination host with cpu_shared_set should be updated to use
+ the destination cpu_shared_set.
+ """
+ self.server = self._create_server(host='src', networks='none')
+
+ conn = self.src.driver._host.get_connection()
+ dom = conn.lookupByUUIDString(self.server['id'])
+ xml = dom.XMLDesc(0)
+ self.assertIn('1', xml)
+
+ self.flags(cpu_shared_set='0,1', group='compute')
+ self.restart_compute_service('dest')
+ self._live_migrate(self.server, 'completed')
+ self.assertEqual('dest', self.get_host(self.server['id']))
+
+ conn = self.dest.driver._host.get_connection()
+ dom = conn.lookupByUUIDString(self.server['id'])
+ xml = dom.XMLDesc(0)
+ # The destination should be updated to "0-1".
+ self.assertIn('1', xml)
+ self.assertNotIn('1', xml)
diff --git a/nova/tests/unit/fixtures/test_libvirt.py b/nova/tests/unit/fixtures/test_libvirt.py
index d5be81af43cd..905a12fda83a 100644
--- a/nova/tests/unit/fixtures/test_libvirt.py
+++ b/nova/tests/unit/fixtures/test_libvirt.py
@@ -172,7 +172,7 @@ class FakeLibvirtTests(test.NoDBTestCase):
self.assertEqual(info[0], libvirt.VIR_DOMAIN_RUNNING)
self.assertEqual(info[1], 128000)
self.assertLessEqual(info[2], 128000)
- self.assertEqual(info[3], 1)
+ self.assertEqual(info[3]['number'], 1)
self.assertIs(type(info[4]), int)
def test_createXML_runs_domain(self):