RT: Migration resource tracking uses migration context

Now that we claim and stash the claimed resources (currently only NUMA
topology is tracked this way).

This patch fixes the migration of instances with NUMA requirements as
they will now be properly tracked and subsequent boot/migrate requests
will see a consistent view of resources.

Change-Id: I16137ed3a34622e1edaa61f1793fe38ea00ec251
Partial-bug: #1417667
Related-blueprint: migration-fix-resource-tracking
This commit is contained in:
Nikola Dipanov 2015-08-31 17:14:36 +01:00
parent a298455398
commit 7fd3032f39
2 changed files with 77 additions and 19 deletions

View File

@ -326,10 +326,9 @@ class ResourceTracker(object):
elif not isinstance(image_meta, objects.ImageMeta):
image_meta = objects.ImageMeta.from_dict(image_meta)
if (instance_type is not None and
instance_type.id == itype['id']):
numa_topology = hardware.numa_get_constraints(
itype, image_meta)
if (instance_type is not None and instance_type.id == itype['id']):
numa_topology = self._get_migration_context_resource(
'numa_topology', instance)
usage = self._get_usage_dict(
itype, numa_topology=numa_topology)
if self.pci_tracker:
@ -677,6 +676,14 @@ class ResourceTracker(object):
# those for now to avoid them being included in below calculations.
return migration.migration_type in ('resize', 'migration')
def _get_migration_context_resource(self, resource, instance,
prefix='new_', itype=None):
migration_context = instance.migration_context
if migration_context:
return getattr(migration_context, prefix + resource)
else:
return None
def _update_usage_from_migration(self, context, instance, image_meta,
migration):
"""Update usage for a single migration. The record may
@ -696,6 +703,7 @@ class ResourceTracker(object):
record = self.tracked_instances.get(uuid, None)
itype = None
numa_topology = None
if same_node:
# same node resize. record usage for whichever instance type the
@ -704,21 +712,29 @@ class ResourceTracker(object):
migration.old_instance_type_id):
itype = self._get_instance_type(context, instance, 'new_',
migration.new_instance_type_id)
numa_topology = self._get_migration_context_resource(
'numa_topology', instance)
else:
# instance record already has new flavor, hold space for a
# possible revert to the old instance type:
itype = self._get_instance_type(context, instance, 'old_',
migration.old_instance_type_id)
numa_topology = self._get_migration_context_resource(
'numa_topology', instance, prefix='old_')
elif incoming and not record:
# instance has not yet migrated here:
itype = self._get_instance_type(context, instance, 'new_',
migration.new_instance_type_id)
numa_topology = self._get_migration_context_resource(
'numa_topology', instance)
elif outbound and not record:
# instance migrated, but record usage for a possible revert:
itype = self._get_instance_type(context, instance, 'old_',
migration.old_instance_type_id)
numa_topology = self._get_migration_context_resource(
'numa_topology', instance, prefix='old_')
if image_meta is None:
image_meta = objects.ImageMeta.from_instance(instance)
@ -728,14 +744,6 @@ class ResourceTracker(object):
image_meta = objects.ImageMeta.from_dict(image_meta)
if itype:
host_topology = self.compute_node.get('numa_topology')
if host_topology:
host_topology = objects.NUMATopology.obj_from_db_obj(
host_topology)
numa_topology = hardware.numa_get_constraints(itype, image_meta)
numa_topology = (
hardware.numa_fit_instance_to_host(
host_topology, numa_topology))
usage = self._get_usage_dict(
itype, numa_topology=numa_topology)
if self.pci_tracker:

View File

@ -246,7 +246,7 @@ _MIGRATION_INSTANCE_FIXTURES = {
vcpus=_INSTANCE_TYPE_FIXTURES[1]['vcpus'],
root_gb=_INSTANCE_TYPE_FIXTURES[1]['root_gb'],
ephemeral_gb=_INSTANCE_TYPE_FIXTURES[1]['ephemeral_gb'],
numa_topology=None,
numa_topology=_INSTANCE_NUMA_TOPOLOGIES['2mb'],
instance_type_id=1,
vm_state=vm_states.ACTIVE,
power_state=power_state.RUNNING,
@ -315,6 +315,16 @@ _MIGRATION_CONTEXT_FIXTURES = {
migration_id=3,
new_numa_topology=None,
old_numa_topology=None),
'f15ecfb0-9bf6-42db-9837-706eb2c4bf08': objects.MigrationContext(
instance_uuid='f15ecfb0-9bf6-42db-9837-706eb2c4bf08',
migration_id=1,
new_numa_topology=None,
old_numa_topology=_INSTANCE_NUMA_TOPOLOGIES['2mb']),
'f6ed631a-8645-4b12-8e1e-2fff55795765': objects.MigrationContext(
instance_uuid='f6ed631a-8645-4b12-8e1e-2fff55795765',
migration_id=2,
new_numa_topology=_INSTANCE_NUMA_TOPOLOGIES['2mb'],
old_numa_topology=None),
}
@ -629,7 +639,9 @@ class TestUpdateAvailableResources(BaseTestCase):
# processing code, and this property calls
# objects.Instance.get_by_uuid, so we have the migration return
inst_uuid = migr_obj.instance_uuid
get_inst_mock.return_value = _MIGRATION_INSTANCE_FIXTURES[inst_uuid]
instance = _MIGRATION_INSTANCE_FIXTURES[inst_uuid].obj_clone()
get_inst_mock.return_value = instance
instance.migration_context = _MIGRATION_CONTEXT_FIXTURES[inst_uuid]
update_mock = self._update_available_resources()
@ -686,8 +698,10 @@ class TestUpdateAvailableResources(BaseTestCase):
migr_obj = _MIGRATION_FIXTURES['dest-only']
migr_mock.return_value = [migr_obj]
inst_uuid = migr_obj.instance_uuid
get_inst_mock.return_value = _MIGRATION_INSTANCE_FIXTURES[inst_uuid]
instance = _MIGRATION_INSTANCE_FIXTURES[inst_uuid].obj_clone()
get_inst_mock.return_value = instance
get_cn_mock.return_value = _COMPUTE_NODE_FIXTURES[0]
instance.migration_context = _MIGRATION_CONTEXT_FIXTURES[inst_uuid]
update_mock = self._update_available_resources()
@ -721,13 +735,16 @@ class TestUpdateAvailableResources(BaseTestCase):
self.assertTrue(obj_base.obj_equal_prims(expected_resources,
self.rt.compute_node))
@mock.patch('nova.objects.MigrationContext.get_by_instance_uuid',
return_value=None)
@mock.patch('nova.objects.ComputeNode.get_by_host_and_nodename')
@mock.patch('nova.objects.MigrationList.get_in_progress_by_host_and_node')
@mock.patch('nova.objects.Instance.get_by_uuid')
@mock.patch('nova.objects.InstanceList.get_by_host_and_node')
def test_some_instances_source_and_dest_migration(self, get_mock,
get_inst_mock, migr_mock,
get_cn_mock):
get_cn_mock,
get_mig_ctxt_mock):
# We test the behavior of update_available_resource() when
# there is an active migration that involves this compute node
# as the destination host AND the source host, and the resource
@ -744,7 +761,9 @@ class TestUpdateAvailableResources(BaseTestCase):
inst_uuid = migr_obj.instance_uuid
# The resizing instance has already had its instance type
# changed to the *new* instance type (the bigger one, instance type 2)
resizing_instance = _MIGRATION_INSTANCE_FIXTURES[inst_uuid]
resizing_instance = _MIGRATION_INSTANCE_FIXTURES[inst_uuid].obj_clone()
resizing_instance.migration_context = (
_MIGRATION_CONTEXT_FIXTURES[resizing_instance.uuid])
all_instances = _INSTANCE_FIXTURES + [resizing_instance]
get_mock.return_value = all_instances
get_inst_mock.return_value = resizing_instance
@ -1257,6 +1276,7 @@ class TestMoveClaim(BaseTestCase):
self.driver_mock.get_host_ip_addr.return_value = "fake-ip"
flavor_mock.return_value = objects.Flavor(**self.flavor)
mig_context_obj = _MIGRATION_CONTEXT_FIXTURES[self.instance.uuid]
self.instance.migration_context = mig_context_obj
expected = copy.deepcopy(self.rt.compute_node)
self.adjust_expected(expected, self.flavor)
@ -1288,6 +1308,7 @@ class TestMoveClaim(BaseTestCase):
self.instance = _MIGRATION_INSTANCE_FIXTURES[migr_obj['instance_uuid']]
self.instance._context = self.ctx
mig_context_obj = _MIGRATION_CONTEXT_FIXTURES[self.instance.uuid]
self.instance.migration_context = mig_context_obj
with mock.patch.object(self.instance, 'save'):
self.rt.instance_claim(self.ctx, self.instance, None)
@ -1327,11 +1348,19 @@ class TestMoveClaim(BaseTestCase):
# Get our migrations, instances and itypes in a row
src_migr = _MIGRATION_FIXTURES['source-only']
src_instance = _MIGRATION_INSTANCE_FIXTURES[src_migr['instance_uuid']]
src_instance = (
_MIGRATION_INSTANCE_FIXTURES[src_migr['instance_uuid']].obj_clone()
)
src_instance.migration_context = (
_MIGRATION_CONTEXT_FIXTURES[src_instance.uuid])
old_itype = _INSTANCE_TYPE_FIXTURES[src_migr['old_instance_type_id']]
dst_migr = _MIGRATION_FIXTURES['dest-only']
dst_instance = _MIGRATION_INSTANCE_FIXTURES[dst_migr['instance_uuid']]
dst_instance = (
_MIGRATION_INSTANCE_FIXTURES[dst_migr['instance_uuid']].obj_clone()
)
new_itype = _INSTANCE_TYPE_FIXTURES[dst_migr['new_instance_type_id']]
dst_instance.migration_context = (
_MIGRATION_CONTEXT_FIXTURES[dst_instance.uuid])
# Set up the destination resource tracker
# update_available_resource to initialise extensible resource trackers
@ -1377,6 +1406,25 @@ class TestMoveClaim(BaseTestCase):
self.assertTrue(obj_base.obj_equal_prims(expected,
src_rt.compute_node))
def test_update_available_resources_migration_no_context(self, pci_mock,
inst_list_mock, inst_by_uuid, migr_mock, inst_save_mock):
"""When migrating onto older nodes - it is possible for the
migration_context record to be missing. Confirm resource audit works
regardless.
"""
self.register_mocks(pci_mock, inst_list_mock, inst_by_uuid, migr_mock,
inst_save_mock)
migr_obj = _MIGRATION_FIXTURES['source-and-dest']
self.instance = _MIGRATION_INSTANCE_FIXTURES[migr_obj['instance_uuid']]
self.instance.migration_context = None
expected = copy.deepcopy(self.rt.compute_node)
self.adjust_expected(expected, self.flavor)
self.audit(self.rt, [], [migr_obj], self.instance)
self.assertTrue(obj_base.obj_equal_prims(expected,
self.rt.compute_node))
def test_dupe_filter(self, pci_mock, inst_list_mock, inst_by_uuid,
migr_mock, inst_save_mock):
self.register_mocks(pci_mock, inst_list_mock, inst_by_uuid, migr_mock,
@ -1386,6 +1434,8 @@ class TestMoveClaim(BaseTestCase):
# This is good enough to prevent a lazy-load; value is unimportant
migr_obj['updated_at'] = None
self.instance = _MIGRATION_INSTANCE_FIXTURES[migr_obj['instance_uuid']]
self.instance.migration_context = (
_MIGRATION_CONTEXT_FIXTURES[self.instance.uuid])
self.audit(self.rt, [], [migr_obj, migr_obj], self.instance)
self.assertEqual(1, len(self.rt.tracked_migrations))