New objects for NUMA live migration

This patch contains the Nova object modifications that will eventually
enable NUMA live migration:

* (src|dst)_supports_numa_live_migration are added to
LibvirtLiveMigrateData. They will be used by the source and
destination hosts to signal to each other that they can perform a
NUMA-aware live migration.

* LibvirtLiveMigrateNUMAInfo is a new object that encodes how the
instance's NUMA-y things will fit on the destination host. It contains
things like the new CPU pins, the new NUMA cell pins, and the new
emulator thread pins.

* dst_numa_info is added to LibvirtLiveMigrateData. It contains a
LibvirtLiveMigrateNUMAInfo object. It will be set by the destination
and will be used by the source to update the instance's XML.

Implements blueprint numa-aware-live-migration
Change-Id: I7c7f54ab142f01808ee4b46d27b4a336e476cc77
This commit is contained in:
Artom Lifshitz 2019-02-04 22:42:26 +00:00
parent 7a18209a81
commit 71fafa5b19
4 changed files with 81 additions and 10 deletions

View File

@ -1262,3 +1262,7 @@ class ListOfListsOfStringsField(fields.AutoTypedField):
# when https://review.opendev.org/#/c/634700/ is released.
class ListOfUUIDField(AutoTypedField):
AUTO_TYPE = List(fields.UUID())
class DictOfSetOfIntegersField(fields.AutoTypedField):
AUTO_TYPE = Dict(Set(fields.Integer()))

View File

@ -109,6 +109,26 @@ class VIFMigrateData(obj_base.NovaObject):
return vif_mig_data
@obj_base.NovaObjectRegistry.register
class LibvirtLiveMigrateNUMAInfo(obj_base.NovaObject):
# Version 1.0: Initial version
VERSION = '1.0'
fields = {
# NOTE(artom) We need a 1:many cardinality here, so DictOfIntegers with
# its 1:1 cardinality cannot work here. cpu_pins can have a single
# guest CPU pinned to multiple host CPUs.
'cpu_pins': fields.DictOfSetOfIntegersField(),
# NOTE(artom) Currently we never pin a guest cell to more than a single
# host cell, so cell_pins could be a DictOfIntegers, but
# DictOfSetOfIntegers is more future-proof.
'cell_pins': fields.DictOfSetOfIntegersField(),
'emulator_pins': fields.SetOfIntegersField(),
'sched_vcpus': fields.SetOfIntegersField(),
'sched_priority': fields.IntegerField(),
}
@obj_base.NovaObjectRegistry.register_if(False)
class LiveMigrateData(obj_base.NovaObject):
# Version 1.0: Initial version
@ -200,7 +220,9 @@ class LibvirtLiveMigrateData(LiveMigrateData):
# Version 1.7: Added dst_wants_file_backed_memory
# Version 1.8: Added file_backed_memory_discard
# Version 1.9: Inherited vifs from LiveMigrateData
VERSION = '1.9'
# Version 1.10: Added dst_numa_info, src_supports_numa_live_migration, and
# dst_supports_numa_live_migration fields
VERSION = '1.10'
fields = {
'filename': fields.StringField(),
@ -224,12 +246,23 @@ class LibvirtLiveMigrateData(LiveMigrateData):
# file_backed_memory_discard is ignored unless
# dst_wants_file_backed_memory is set
'file_backed_memory_discard': fields.BooleanField(),
# TODO(artom) (src|dst)_supports_numa_live_migration are only used as
# flags to indicate that the compute host is new enough to perform a
# NUMA-aware live migration. Remove in version 2.0.
'src_supports_numa_live_migration': fields.BooleanField(),
'dst_supports_numa_live_migration': fields.BooleanField(),
'dst_numa_info': fields.ObjectField('LibvirtLiveMigrateNUMAInfo'),
}
def obj_make_compatible(self, primitive, target_version):
super(LibvirtLiveMigrateData, self).obj_make_compatible(
primitive, target_version)
target_version = versionutils.convert_version_to_tuple(target_version)
if (target_version < (1, 10) and
'src_supports_numa_live_migration' in primitive):
del primitive['src_supports_numa_live_migration']
if target_version < (1, 10) and 'dst_numa_info' in primitive:
del primitive['dst_numa_info']
if target_version < (1, 9) and 'vifs' in primitive:
del primitive['vifs']
if target_version < (1, 8):

View File

@ -68,6 +68,23 @@ class _TestLibvirtLiveMigrateData(object):
expected_info['boot_index'] = '1'
self.assertEqual(expected_info, obj.as_disk_info())
def test_numa_migrate_data(self):
data = lambda x: x['nova_object.data']
obj = migrate_data.LibvirtLiveMigrateNUMAInfo(
cpu_pins={'0': set([1])},
cell_pins={'2': set([3])},
emulator_pins=set([4]),
sched_vcpus=set([5]),
sched_priority=6)
manifest = ovo_base.obj_tree_get_versions(obj.obj_name())
primitive = data(obj.obj_to_primitive(target_version='1.0',
version_manifest=manifest))
self.assertEqual({'0': (1,)}, primitive['cpu_pins'])
self.assertEqual({'2': (3,)}, primitive['cell_pins'])
self.assertEqual((4,), primitive['emulator_pins'])
self.assertEqual((5,), primitive['sched_vcpus'])
self.assertEqual(6, primitive['sched_priority'])
def test_obj_make_compatible(self):
obj = migrate_data.LibvirtLiveMigrateData(
src_supports_native_luks=True,
@ -76,30 +93,46 @@ class _TestLibvirtLiveMigrateData(object):
serial_listen_addr='127.0.0.1',
target_connect_addr='127.0.0.1',
dst_wants_file_backed_memory=False,
file_backed_memory_discard=False)
file_backed_memory_discard=False,
src_supports_numa_live_migraton=True,
dst_supports_numa_live_migraton=True,
dst_numa_info=migrate_data.LibvirtLiveMigrateNUMAInfo())
manifest = ovo_base.obj_tree_get_versions(obj.obj_name())
data = lambda x: x['nova_object.data']
primitive = data(obj.obj_to_primitive())
self.assertIn('file_backed_memory_discard', primitive)
primitive = data(obj.obj_to_primitive(target_version='1.0'))
primitive = data(obj.obj_to_primitive(target_version='1.0',
version_manifest=manifest))
self.assertNotIn('target_connect_addr', primitive)
self.assertNotIn('supported_perf_events', primitive)
self.assertNotIn('old_vol_attachment_ids', primitive)
self.assertNotIn('src_supports_native_luks', primitive)
self.assertNotIn('dst_wants_file_backed_memory', primitive)
primitive = data(obj.obj_to_primitive(target_version='1.1'))
primitive = data(obj.obj_to_primitive(target_version='1.1',
version_manifest=manifest))
self.assertNotIn('serial_listen_ports', primitive)
primitive = data(obj.obj_to_primitive(target_version='1.2'))
primitive = data(obj.obj_to_primitive(target_version='1.2',
version_manifest=manifest))
self.assertNotIn('supported_perf_events', primitive)
primitive = data(obj.obj_to_primitive(target_version='1.3'))
primitive = data(obj.obj_to_primitive(target_version='1.3',
version_manifest=manifest))
self.assertNotIn('old_vol_attachment_ids', primitive)
primitive = data(obj.obj_to_primitive(target_version='1.4'))
primitive = data(obj.obj_to_primitive(target_version='1.4',
version_manifest=manifest))
self.assertNotIn('src_supports_native_luks', primitive)
primitive = data(obj.obj_to_primitive(target_version='1.6'))
primitive = data(obj.obj_to_primitive(target_version='1.6',
version_manifest=manifest))
self.assertNotIn('dst_wants_file_backed_memory', primitive)
primitive = data(obj.obj_to_primitive(target_version='1.7'))
primitive = data(obj.obj_to_primitive(target_version='1.7',
version_manifest=manifest))
self.assertNotIn('file_backed_memory_discard', primitive)
primitive = data(obj.obj_to_primitive(target_version='1.9',
version_manifest=manifest))
self.assertNotIn('dst_numa_info', primitive)
self.assertNotIn('src_supports_numa_live_migration', primitive)
self.assertNotIn('dst_supports_numa_live_migration', primitive)
def test_bdm_obj_make_compatible(self):
obj = migrate_data.LibvirtLiveMigrateBDMInfo(

View File

@ -1092,7 +1092,8 @@ object_data = {
'KeyPair': '1.4-1244e8d1b103cc69d038ed78ab3a8cc6',
'KeyPairList': '1.3-94aad3ac5c938eef4b5e83da0212f506',
'LibvirtLiveMigrateBDMInfo': '1.1-5f4a68873560b6f834b74e7861d71aaf',
'LibvirtLiveMigrateData': '1.9-7082cc7dd48ca49df71fe3846521b2f3',
'LibvirtLiveMigrateData': '1.10-348cf70ea44d3b985f45f64725d6f6a7',
'LibvirtLiveMigrateNUMAInfo': '1.0-0e777677f3459d0ed1634eabbdb6c22f',
'MemoryDiagnostics': '1.0-2c995ae0f2223bb0f8e523c5cc0b83da',
'Migration': '1.6-fd6b1abfd8e3ce945348e7b5f04baa28',
'MigrationContext': '1.1-9fb17b0b521370957a884636499df52d',