rt: generalize claim code to be useful for other move actions

Make sure that we can re-use all the code that is needed for doing
claims for live migration and evacuate.

Since we now pass the migration_type to the method that creates it in
the resource tracker, we also re-use some logic that was already in
there to make sure we migration_type is always consistent.

Change-Id: Ia47a70c009d4dad1d3f832936f57ce68c587bd44
Related-blueprint: migration-fix-resource-tracking
This commit is contained in:
Nikola Dipanov 2015-06-30 16:23:50 +01:00
parent b0854ba0c6
commit a414212820
4 changed files with 76 additions and 32 deletions

View File

@ -34,6 +34,7 @@ from nova import exception
from nova.i18n import _, _LI, _LW
from nova import objects
from nova.objects import base as obj_base
from nova.objects import migration as migration_obj
from nova.pci import manager as pci_manager
from nova import rpc
from nova.scheduler import client as scheduler_client
@ -162,11 +163,23 @@ class ResourceTracker(object):
@utils.synchronized(COMPUTE_RESOURCE_SEMAPHORE)
def resize_claim(self, context, instance, instance_type,
image_meta=None, limits=None):
"""Indicate that resources are needed for a resize operation to this
compute host.
"""Create a claim for a resize or cold-migration move."""
return self._move_claim(context, instance, instance_type,
image_meta=image_meta, limits=limits)
def _move_claim(self, context, instance, new_instance_type, move_type=None,
image_meta=None, limits=None):
"""Indicate that resources are needed for a move to this host.
Move can be either a migrate/resize, live-migrate or an
evacuate/rebuild operation.
:param context: security context
:param instance: instance object to reserve resources for
:param instance_type: new instance_type being resized to
:param new_instance_type: new instance_type being resized to
:param image_meta: instance image metadata
:param move_type: move type - can be one of 'migration', 'resize',
'live-migration', 'evacuate'
:param limits: Dict of oversubscription limits for memory, disk,
and CPUs
:returns: A Claim ticket representing the reserved resources. This
@ -179,21 +192,21 @@ class ResourceTracker(object):
# compute_driver doesn't support resource tracking, just
# generate the migration record and continue the resize:
migration = self._create_migration(context, instance,
instance_type)
new_instance_type, move_type)
return claims.NopClaim(migration=migration)
# get memory overhead required to build this instance:
overhead = self.driver.estimate_instance_overhead(instance_type)
overhead = self.driver.estimate_instance_overhead(new_instance_type)
LOG.debug("Memory overhead for %(flavor)d MB instance; %(overhead)d "
"MB", {'flavor': instance_type.memory_mb,
"MB", {'flavor': new_instance_type.memory_mb,
'overhead': overhead['memory_mb']})
claim = claims.MoveClaim(context, instance, instance_type,
claim = claims.MoveClaim(context, instance, new_instance_type,
image_meta, self, self.compute_node,
overhead=overhead, limits=limits)
migration = self._create_migration(context, instance,
instance_type)
new_instance_type, move_type)
claim.migration = migration
# Mark the resources in-use for the resize landing on this
@ -205,7 +218,8 @@ class ResourceTracker(object):
return claim
def _create_migration(self, context, instance, instance_type):
def _create_migration(self, context, instance, new_instance_type,
move_type=None):
"""Create a migration record for the upcoming resize. This should
be done while the COMPUTE_RESOURCES_SEMAPHORE is held so the resource
claim will not be lost if the audit process starts.
@ -215,14 +229,16 @@ class ResourceTracker(object):
migration.dest_node = self.nodename
migration.dest_host = self.driver.get_host_ip_addr()
migration.old_instance_type_id = instance.flavor.id
migration.new_instance_type_id = instance_type.id
migration.new_instance_type_id = new_instance_type.id
migration.status = 'pre-migrating'
migration.instance_uuid = instance.uuid
migration.source_compute = instance.host
migration.source_node = instance.node
migration.migration_type = (
migration.old_instance_type_id != migration.new_instance_type_id
and 'resize' or 'migration')
if move_type:
migration.migration_type = move_type
else:
migration.migration_type = migration_obj.determine_migration_type(
migration)
migration.create()
return migration

View File

@ -20,7 +20,7 @@ from nova.objects import fields
from nova import utils
def _determine_migration_type(migration):
def determine_migration_type(migration):
if migration['old_instance_type_id'] != migration['new_instance_type_id']:
return 'resize'
else:
@ -58,7 +58,7 @@ class Migration(base.NovaPersistentObject, base.NovaObject,
for key in migration.fields:
value = db_migration[key]
if key == 'migration_type' and value is None:
value = _determine_migration_type(db_migration)
value = determine_migration_type(db_migration)
migration[key] = value
migration._context = context
@ -77,7 +77,7 @@ class Migration(base.NovaPersistentObject, base.NovaObject,
if attrname == 'migration_type':
# NOTE(danms): The only reason we'd need to load this is if
# some older node sent us one. So, guess the type.
self.migration_type = _determine_migration_type(self)
self.migration_type = determine_migration_type(self)
elif attrname == 'hidden':
self.hidden = False
else:
@ -100,6 +100,11 @@ class Migration(base.NovaPersistentObject, base.NovaObject,
raise exception.ObjectActionError(action='create',
reason='already created')
updates = self.obj_get_changes()
if 'migration_type' not in updates:
raise exception.ObjectActionError(
action="create",
reason="cannot create a Migration object without a "
"migration_type set")
db_migration = db.migration_create(self._context, updates)
self._from_db_object(self._context, self, db_migration)

View File

@ -1156,19 +1156,20 @@ class InstanceClaimTestCase(BaseTrackerTestCase):
self.assertEqual('fakenode', instance['node'])
class MoveClaimTestCase(BaseTrackerTestCase):
class _MoveClaimTestCase(BaseTrackerTestCase):
def setUp(self):
super(MoveClaimTestCase, self).setUp()
super(_MoveClaimTestCase, self).setUp()
self.instance = self._fake_instance_obj()
self.instance_type = self._fake_flavor_create()
self.claim_method = self.tracker._move_claim
@mock.patch('nova.objects.InstancePCIRequests.get_by_instance_uuid',
return_value=objects.InstancePCIRequests(requests=[]))
def test_claim(self, mock_get):
self.tracker.resize_claim(self.context, self.instance,
self.instance_type, self.limits)
self.claim_method(self.context, self.instance,
self.instance_type, limits=self.limits)
self._assert(FAKE_VIRT_MEMORY_WITH_OVERHEAD, 'memory_mb_used')
self._assert(FAKE_VIRT_LOCAL_GB, 'local_gb_used')
self._assert(FAKE_VIRT_VCPUS, 'vcpus_used')
@ -1178,8 +1179,8 @@ class MoveClaimTestCase(BaseTrackerTestCase):
return_value=objects.InstancePCIRequests(requests=[]))
def test_abort(self, mock_get):
try:
with self.tracker.resize_claim(self.context, self.instance,
self.instance_type, self.limits):
with self.claim_method(self.context, self.instance,
self.instance_type, limits=self.limits):
raise test.TestingException("abort")
except test.TestingException:
pass
@ -1197,11 +1198,11 @@ class MoveClaimTestCase(BaseTrackerTestCase):
2 * FAKE_VIRT_MEMORY_WITH_OVERHEAD,
2 * FAKE_VIRT_LOCAL_GB,
2 * FAKE_VIRT_VCPUS)
self.tracker.resize_claim(self.context, self.instance,
self.instance_type, limits)
self.claim_method(
self.context, self.instance, self.instance_type, limits=limits)
instance2 = self._fake_instance_obj()
self.tracker.resize_claim(self.context, instance2, self.instance_type,
limits)
self.claim_method(
self.context, instance2, self.instance_type, limits=limits)
self._assert(2 * FAKE_VIRT_MEMORY_WITH_OVERHEAD, 'memory_mb_used')
self._assert(2 * FAKE_VIRT_LOCAL_GB, 'local_gb_used')
@ -1210,8 +1211,9 @@ class MoveClaimTestCase(BaseTrackerTestCase):
@mock.patch('nova.objects.InstancePCIRequests.get_by_instance_uuid',
return_value=objects.InstancePCIRequests(requests=[]))
def test_revert(self, mock_get):
self.tracker.resize_claim(self.context, self.instance,
self.instance_type, {}, self.limits)
self.claim_method(
self.context, self.instance, self.instance_type,
image_meta={}, limits=self.limits)
self.tracker.drop_move_claim(self.context, self.instance)
self.assertEqual(0, len(self.tracker.tracked_instances))
@ -1239,6 +1241,13 @@ class MoveClaimTestCase(BaseTrackerTestCase):
self.assertTrue(result)
class ResizeClaimTestCase(_MoveClaimTestCase):
def setUp(self):
super(ResizeClaimTestCase, self).setUp()
self.claim_method = self.tracker.resize_claim
class OrphanTestCase(BaseTrackerTestCase):
def _driver(self):
class OrphanVirtDriver(FakeVirtDriver):

View File

@ -79,11 +79,13 @@ class _TestMigrationObject(object):
ctxt = context.get_admin_context()
fake_migration = fake_db_migration()
self.mox.StubOutWithMock(db, 'migration_create')
db.migration_create(ctxt, {'source_compute': 'foo'}).AndReturn(
fake_migration)
db.migration_create(ctxt, {'source_compute': 'foo',
'migration_type': 'resize'}
).AndReturn(fake_migration)
self.mox.ReplayAll()
mig = migration.Migration(context=ctxt)
mig.source_compute = 'foo'
mig.migration_type = 'resize'
mig.create()
self.assertEqual(fake_migration['dest_compute'], mig.dest_compute)
@ -91,14 +93,26 @@ class _TestMigrationObject(object):
ctxt = context.get_admin_context()
fake_migration = fake_db_migration()
self.mox.StubOutWithMock(db, 'migration_create')
db.migration_create(ctxt, {'source_compute': 'foo'}).AndReturn(
fake_migration)
db.migration_create(ctxt, {'source_compute': 'foo',
'migration_type': 'resize'}
).AndReturn(fake_migration)
self.mox.ReplayAll()
mig = migration.Migration(context=ctxt)
mig.source_compute = 'foo'
mig.migration_type = 'resize'
mig.create()
self.assertRaises(exception.ObjectActionError, mig.create)
def test_create_fails_migration_type(self):
ctxt = context.get_admin_context()
self.mox.StubOutWithMock(db, 'migration_create')
self.mox.ReplayAll()
mig = migration.Migration(context=ctxt,
old_instance_type_id=42,
new_instance_type_id=84)
mig.source_compute = 'foo'
self.assertRaises(exception.ObjectActionError, mig.create)
def test_save(self):
ctxt = context.get_admin_context()
fake_migration = fake_db_migration()