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:
parent
b0854ba0c6
commit
a414212820
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue