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:
parent
a298455398
commit
7fd3032f39
|
@ -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:
|
||||
|
|
|
@ -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))
|
||||
|
||||
|
|
Loading…
Reference in New Issue