From a2ba455e2d2bc41f4a80a08d5434b741ed715ef4 Mon Sep 17 00:00:00 2001 From: Dan Smith Date: Mon, 12 Aug 2013 15:55:04 -0700 Subject: [PATCH] Make compute_api migrate/resize paths use instance objects This converts the migrate/resize/live_migrate paths to use instance objects. This also syncs up cells rpcapi and compute task rpcapi so we can ditch the special cells code in compute/cells_api.py. Related to blueprint compute-api-objects Change-Id: I977a5edb8a5313b2a8827796ff8c2bc081f9d63f --- .../compute/contrib/admin_actions.py | 4 +- .../compute/plugins/v3/admin_actions.py | 4 +- nova/cells/manager.py | 16 +- nova/cells/messaging.py | 31 +++ nova/cells/rpcapi.py | 23 ++ nova/compute/api.py | 74 ++++--- nova/compute/cells_api.py | 67 +----- nova/compute/manager.py | 9 +- nova/compute/rpcapi.py | 26 ++- nova/conductor/api.py | 38 +++- nova/conductor/manager.py | 21 +- nova/conductor/rpcapi.py | 12 +- nova/conductor/tasks/live_migrate.py | 24 ++- .../compute/contrib/test_admin_actions.py | 15 +- .../compute/plugins/v3/test_admin_actions.py | 27 +-- nova/tests/cells/test_cells_manager.py | 20 ++ nova/tests/cells/test_cells_messaging.py | 16 ++ nova/tests/cells/test_cells_rpcapi.py | 30 +++ nova/tests/compute/test_compute.py | 141 +----------- nova/tests/compute/test_compute_api.py | 93 ++++---- nova/tests/compute/test_compute_mgr.py | 94 ++++++++ nova/tests/compute/test_rpcapi.py | 6 +- .../conductor/tasks/test_live_migrate.py | 20 +- nova/tests/conductor/test_conductor.py | 202 ++++++++++-------- nova/tests/objects/test_migration.py | 47 ++-- 25 files changed, 621 insertions(+), 439 deletions(-) diff --git a/nova/api/openstack/compute/contrib/admin_actions.py b/nova/api/openstack/compute/contrib/admin_actions.py index 688c5d4f9615..4c05278119ff 100644 --- a/nova/api/openstack/compute/contrib/admin_actions.py +++ b/nova/api/openstack/compute/contrib/admin_actions.py @@ -127,7 +127,7 @@ class AdminActionsController(wsgi.Controller): context = req.environ['nova.context'] authorize(context, 'migrate') try: - instance = self.compute_api.get(context, id) + instance = self.compute_api.get(context, id, want_objects=True) self.compute_api.resize(req.environ['nova.context'], instance) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, @@ -293,7 +293,7 @@ class AdminActionsController(wsgi.Controller): raise exc.HTTPBadRequest(explanation=msg) try: - instance = self.compute_api.get(context, id) + instance = self.compute_api.get(context, id, want_objects=True) self.compute_api.live_migrate(context, instance, block_migration, disk_over_commit, host) except (exception.ComputeServiceUnavailable, diff --git a/nova/api/openstack/compute/plugins/v3/admin_actions.py b/nova/api/openstack/compute/plugins/v3/admin_actions.py index 820c31f59056..5384c21c8f65 100644 --- a/nova/api/openstack/compute/plugins/v3/admin_actions.py +++ b/nova/api/openstack/compute/plugins/v3/admin_actions.py @@ -122,7 +122,7 @@ class AdminActionsController(wsgi.Controller): context = req.environ['nova.context'] authorize(context, 'migrate') try: - instance = self.compute_api.get(context, id) + instance = self.compute_api.get(context, id, want_objects=True) self.compute_api.resize(req.environ['nova.context'], instance) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, @@ -287,7 +287,7 @@ class AdminActionsController(wsgi.Controller): raise exc.HTTPBadRequest(explanation=msg) try: - instance = self.compute_api.get(context, id) + instance = self.compute_api.get(context, id, want_objects=True) self.compute_api.live_migrate(context, instance, block_migration, disk_over_commit, host) except (exception.ComputeServiceUnavailable, diff --git a/nova/cells/manager.py b/nova/cells/manager.py index 638e8518055f..e616517caa7d 100644 --- a/nova/cells/manager.py +++ b/nova/cells/manager.py @@ -65,7 +65,7 @@ class CellsManager(manager.Manager): Scheduling requests get passed to the scheduler class. """ - RPC_API_VERSION = '1.19' + RPC_API_VERSION = '1.20' def __init__(self, *args, **kwargs): # Mostly for tests. @@ -488,3 +488,17 @@ class CellsManager(manager.Manager): def soft_delete_instance(self, ctxt, instance): """Soft-delete an instance in its cell.""" self.msg_runner.soft_delete_instance(ctxt, instance) + + def resize_instance(self, ctxt, instance, flavor, + extra_instance_updates): + """Resize an instance in its cell.""" + self.msg_runner.resize_instance(ctxt, instance, + flavor, extra_instance_updates) + + def live_migrate_instance(self, ctxt, instance, block_migration, + disk_over_commit, host_name): + """Live migrate an instance in its cell.""" + self.msg_runner.live_migrate_instance(ctxt, instance, + block_migration, + disk_over_commit, + host_name) diff --git a/nova/cells/messaging.py b/nova/cells/messaging.py index 1436709a4c22..b8b0167e6477 100644 --- a/nova/cells/messaging.py +++ b/nova/cells/messaging.py @@ -870,6 +870,20 @@ class _TargetedMessageMethods(_BaseMessageMethods): """Unpause an instance via compute_api.pause().""" self._call_compute_api_with_obj(message.ctxt, instance, 'unpause') + def resize_instance(self, message, instance, flavor, + extra_instance_updates): + """Resize an instance via compute_api.resize().""" + self._call_compute_api_with_obj(message.ctxt, instance, 'resize', + flavor_id=flavor['id'], + **extra_instance_updates) + + def live_migrate_instance(self, message, instance, block_migration, + disk_over_commit, host_name): + """Live migrate an instance via compute_api.live_migrate().""" + self._call_compute_api_with_obj(message.ctxt, instance, + 'live_migrate', block_migration, + disk_over_commit, host_name) + class _BroadcastMessageMethods(_BaseMessageMethods): """These are the methods that can be called as a part of a broadcast @@ -1625,6 +1639,23 @@ class MessageRunner(object): """Unpause an instance in its cell.""" self._instance_action(ctxt, instance, 'unpause_instance') + def resize_instance(self, ctxt, instance, flavor, + extra_instance_updates): + """Resize an instance in its cell.""" + extra_kwargs = dict(flavor=flavor, + extra_instance_updates=extra_instance_updates) + self._instance_action(ctxt, instance, 'resize_instance', + extra_kwargs=extra_kwargs) + + def live_migrate_instance(self, ctxt, instance, block_migration, + disk_over_commit, host_name): + """Live migrate an instance in its cell.""" + extra_kwargs = dict(block_migration=block_migration, + disk_over_commit=disk_over_commit, + host_name=host_name) + self._instance_action(ctxt, instance, 'live_migrate_instance', + extra_kwargs=extra_kwargs) + @staticmethod def get_message_types(): return _CELL_MESSAGE_TYPE_TO_MESSAGE_CLS.keys() diff --git a/nova/cells/rpcapi.py b/nova/cells/rpcapi.py index 319b7877b79c..ad1c960a7d04 100644 --- a/nova/cells/rpcapi.py +++ b/nova/cells/rpcapi.py @@ -76,6 +76,7 @@ class CellsAPI(rpc_proxy.RpcProxy): 1.17 - Adds get_host_uptime() 1.18 - Adds terminate_instance() and soft_delete_instance() 1.19 - Adds pause_instance() and unpause_instance() + 1.20 - Adds resize_instance() and live_migrate_instance() ''' BASE_RPC_API_VERSION = '1.0' @@ -515,3 +516,25 @@ class CellsAPI(rpc_proxy.RpcProxy): self.cast(ctxt, self.make_msg('soft_delete_instance', instance=instance), version='1.18') + + def resize_instance(self, ctxt, instance, extra_instance_updates, + scheduler_hint, flavor, reservations): + if not CONF.cells.enable: + return + flavor_p = jsonutils.to_primitive(flavor) + self.cast(ctxt, + self.make_msg('resize_instance', instance=instance, + flavor=flavor_p, + extra_instance_updates=extra_instance_updates), + version='1.20') + + def live_migrate_instance(self, ctxt, instance, host_name, + block_migration, disk_over_commit): + if not CONF.cells.enable: + return + self.cast(ctxt, + self.make_msg('live_migrate_instance', instance=instance, + block_migration=block_migration, + disk_over_commit=disk_over_commit, + host_name=host_name), + version='1.20') diff --git a/nova/compute/api.py b/nova/compute/api.py index 92f52a48b331..8141de341c35 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -56,6 +56,7 @@ from nova.objects import instance as instance_obj from nova.objects import instance_action from nova.objects import instance_info_cache from nova.objects import keypair as keypair_obj +from nova.objects import migration as migration_obj from nova.objects import security_group from nova.openstack.common import excutils from nova.openstack.common.gettextutils import _ @@ -2044,15 +2045,14 @@ class API(base.Base): """Reverts a resize, deleting the 'new' instance in the process.""" elevated = context.elevated() migration_ref = self.db.migration_get_by_instance_and_status(elevated, - instance['uuid'], 'finished') + instance.uuid, 'finished') # reverse quota reservation for increased resource usage deltas = self._reverse_upsize_quota_delta(context, migration_ref) reservations = self._reserve_quota_delta(context, deltas) - instance = self.update(context, instance, - task_state=task_states.RESIZE_REVERTING, - expected_task_state=None) + instance.task_state = task_states.RESIZE_REVERTING + instance.save(expected_task_state=None) self.db.migration_update(elevated, migration_ref['id'], {'status': 'reverting'}) @@ -2168,11 +2168,35 @@ class API(base.Base): return return QUOTAS.reserve(context, project_id=project_id, **deltas) + @staticmethod + def _resize_cells_support(context, reservations, instance, + current_instance_type, new_instance_type): + """Special API cell logic for resize.""" + if reservations: + # With cells, the best we can do right now is commit the + # reservations immediately... + QUOTAS.commit(context, reservations, + project_id=instance.project_id) + # NOTE(johannes/comstud): The API cell needs a local migration + # record for later resize_confirm and resize_reverts to deal + # with quotas. We don't need source and/or destination + # information, just the old and new flavors. Status is set to + # 'finished' since nothing else will update the status along + # the way. + mig = migration_obj.Migration() + mig.instance_uuid = instance.uuid + mig.old_instance_type_id = current_instance_type['id'] + mig.new_instance_type_id = new_instance_type['id'] + mig.status = 'finished' + mig.create(context.elevated()) + @wrap_check_policy @check_instance_lock + @check_instance_cell @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED], task_state=[None]) - def resize(self, context, instance, flavor_id=None, **kwargs): + def resize(self, context, instance, flavor_id=None, + **extra_instance_updates): """Resize (ie, migrate) a running instance. If flavor_id is None, the process is considered a migration, keeping @@ -2207,7 +2231,7 @@ class API(base.Base): # NOTE(sirp): We don't want to force a customer to change their flavor # when Ops is migrating off of a failed host. - if not same_instance_type and new_instance_type['disabled']: + if not same_instance_type and new_instance_type.get('disabled'): raise exception.FlavorNotFound(flavor_id=flavor_id) if same_instance_type and flavor_id: @@ -2241,10 +2265,10 @@ class API(base.Base): used=used, allowed=total_allowed, resource=resource) - instance = self.update(context, instance, - task_state=task_states.RESIZE_PREP, - expected_task_state=None, - progress=0, **kwargs) + instance.task_state = task_states.RESIZE_PREP + instance.progress = 0 + instance.update(extra_instance_updates) + instance.save(expected_task_state=None) filter_properties = {'ignore_hosts': []} @@ -2255,21 +2279,19 @@ class API(base.Base): if (not flavor_id and not CONF.allow_migrate_to_same_host): filter_properties['ignore_hosts'].append(instance['host']) - # With cells, the best we can do right now is commit the reservations - # immediately... - if CONF.cells.enable and reservations: - QUOTAS.commit(context, reservations, - project_id=instance['project_id']) + if self.cell_type == 'api': + # Commit reservations early and create migration record. + self._resize_cells_support(context, reservations, instance, + current_instance_type, + new_instance_type) reservations = [] self._record_action_start(context, instance, instance_actions.RESIZE) scheduler_hint = {'filter_properties': filter_properties} - self.compute_task_api.migrate_server(context, instance, - scheduler_hint=scheduler_hint, - live=False, rebuild=False, flavor=new_instance_type, - block_migration=None, disk_over_commit=None, - reservations=reservations) + self.compute_task_api.resize_instance(context, instance, + extra_instance_updates, scheduler_hint=scheduler_hint, + flavor=new_instance_type, reservations=reservations) @wrap_check_policy @check_instance_lock @@ -2812,6 +2834,7 @@ class API(base.Base): return False + @check_instance_cell @check_instance_state(vm_state=[vm_states.ACTIVE]) def live_migrate(self, context, instance, block_migration, disk_over_commit, host_name): @@ -2819,14 +2842,11 @@ class API(base.Base): LOG.debug(_("Going to try to live migrate instance to %s"), host_name or "another host", instance=instance) - instance = self.update(context, instance, - task_state=task_states.MIGRATING, - expected_task_state=None) + instance.task_state = task_states.MIGRATING + instance.save(expected_task_state=None) - self.compute_task_api.migrate_server(context, instance, - scheduler_hint={'host': host_name}, - live=True, rebuild=False, flavor=None, - block_migration=block_migration, + self.compute_task_api.live_migrate_instance(context, instance, + host_name, block_migration=block_migration, disk_over_commit=disk_over_commit) @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED], diff --git a/nova/compute/cells_api.py b/nova/compute/cells_api.py index 040085e2cdd3..42327fecc2e7 100644 --- a/nova/compute/cells_api.py +++ b/nova/compute/cells_api.py @@ -20,7 +20,6 @@ from nova import block_device from nova.cells import rpcapi as cells_rpcapi from nova.cells import utils as cells_utils from nova.compute import api as compute_api -from nova.compute import flavors from nova.compute import rpcapi as compute_rpcapi from nova.compute import vm_states from nova import exception @@ -68,17 +67,24 @@ class SchedulerRPCAPIRedirect(object): class ConductorTaskRPCAPIRedirect(object): + # NOTE(comstud): These are a list of methods where the cells_rpcapi + # and the compute_task_rpcapi methods have the same signatures. This + # is for transitioning to a common interface where we can just + # swap out the compute_task_rpcapi class with the cells_rpcapi class. + cells_compatible = ['build_instances', 'resize_instance', + 'live_migrate_instance'] + def __init__(self, cells_rpcapi_obj): self.cells_rpcapi = cells_rpcapi_obj def __getattr__(self, key): + if key in self.cells_compatible: + return getattr(self.cells_rpcapi, key) + def _noop_rpc_wrapper(*args, **kwargs): return None return _noop_rpc_wrapper - def build_instances(self, context, **kwargs): - self.cells_rpcapi.build_instances(context, **kwargs) - class ComputeRPCProxyAPI(compute_rpcapi.ComputeAPI): """Class used to substitute Compute RPC API that will proxy @@ -279,49 +285,6 @@ class ComputeCellsAPI(compute_api.API): context, instance, migration_ref=migration_ref) self._cast_to_cells(context, instance, 'confirm_resize') - @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED], - task_state=[None]) - @check_instance_cell - def resize(self, context, instance, flavor_id=None, **kwargs): - """Resize (ie, migrate) a running instance. - - If flavor_id is None, the process is considered a migration, keeping - the original flavor_id. If flavor_id is not None, the instance should - be migrated to a new host and resized to the new flavor_id. - """ - super(ComputeCellsAPI, self).resize(context, instance, - flavor_id=flavor_id, - **kwargs) - - # NOTE(johannes): If we get to this point, then we know the - # specified flavor_id is valid and exists. We'll need to load - # it again, but that should be safe. - - old_instance_type = flavors.extract_flavor(instance) - - if not flavor_id: - new_instance_type = old_instance_type - else: - new_instance_type = flavors.get_flavor_by_flavor_id( - flavor_id, read_deleted="no") - - # NOTE(johannes): Later, when the resize is confirmed or reverted, - # the superclass implementations of those methods will need access - # to a local migration record for quota reasons. We don't need - # source and/or destination information, just the old and new - # flavors. Status is set to 'finished' since nothing else - # will update the status along the way. - self.db.migration_create(context.elevated(), - {'instance_uuid': instance['uuid'], - 'old_instance_type_id': old_instance_type['id'], - 'new_instance_type_id': new_instance_type['id'], - 'status': 'finished'}) - - # FIXME(comstud): pass new instance_type object down to a method - # that'll unfold it - self._cast_to_cells(context, instance, 'resize', flavor_id=flavor_id, - **kwargs) - @check_instance_cell def add_fixed_ip(self, context, instance, *args, **kwargs): """Add fixed_ip from specified network to given instance.""" @@ -512,16 +475,6 @@ class ComputeCellsAPI(compute_api.API): pass return rv - @check_instance_cell - def live_migrate(self, context, instance, block_migration, - disk_over_commit, host_name): - """Migrate a server lively to a new host.""" - super(ComputeCellsAPI, self).live_migrate(context, - instance, block_migration, disk_over_commit, host_name) - - self._cast_to_cells(context, instance, 'live_migrate', - block_migration, disk_over_commit, host_name) - def get_migrations(self, context, filters): return self.cells_rpcapi.get_migrations(context, filters) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 489e18e50529..22a7da6237bf 100755 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -376,7 +376,7 @@ class ComputeVirtAPI(virtapi.VirtAPI): class ComputeManager(manager.SchedulerDependentManager): """Manages the running instances from creation to destruction.""" - RPC_API_VERSION = '2.37' + RPC_API_VERSION = '2.38' def __init__(self, compute_driver=None, *args, **kwargs): """Load configuration options and connect to the hypervisor.""" @@ -3680,6 +3680,7 @@ class ComputeManager(manager.SchedulerDependentManager): """ return self.driver.check_instance_shared_storage_remote(ctxt, data) + @object_compat @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id()) def check_can_live_migrate_destination(self, ctxt, instance, block_migration=False, @@ -3695,7 +3696,7 @@ class ComputeManager(manager.SchedulerDependentManager): :param disk_over_commit: if true, allow disk over commit :returns: a dict containing migration info """ - src_compute_info = self._get_compute_info(ctxt, instance['host']) + src_compute_info = self._get_compute_info(ctxt, instance.host) dst_compute_info = self._get_compute_info(ctxt, CONF.host) dest_check_data = self.driver.check_can_live_migrate_destination(ctxt, instance, src_compute_info, dst_compute_info, @@ -3712,6 +3713,7 @@ class ComputeManager(manager.SchedulerDependentManager): migrate_data.update(dest_check_data['migrate_data']) return migrate_data + @object_compat @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id()) def check_can_live_migrate_source(self, ctxt, instance, dest_check_data): """Check if it is possible to execute live migration. @@ -3725,7 +3727,8 @@ class ComputeManager(manager.SchedulerDependentManager): :returns: a dict containing migration info """ capi = self.conductor_api - bdms = capi.block_device_mapping_get_all_by_instance(ctxt, instance) + instance_p = obj_base.obj_to_primitive(instance) + bdms = capi.block_device_mapping_get_all_by_instance(ctxt, instance_p) is_volume_backed = self.compute_api.is_volume_backed_instance(ctxt, instance, diff --git a/nova/compute/rpcapi.py b/nova/compute/rpcapi.py index 35b2455bae9c..197934894c73 100644 --- a/nova/compute/rpcapi.py +++ b/nova/compute/rpcapi.py @@ -192,6 +192,8 @@ class ComputeAPI(nova.openstack.common.rpc.proxy.RpcProxy): 2.36 - Made pause_instance() and unpause_instance() take new-world instance objects 2.37 - Added the leagacy_bdm_in_spec parameter to run_instance + 2.38 - Made check_can_live_migrate_[destination|source] take + new-world instance objects ''' # @@ -265,22 +267,34 @@ class ComputeAPI(nova.openstack.common.rpc.proxy.RpcProxy): def check_can_live_migrate_destination(self, ctxt, instance, destination, block_migration, disk_over_commit): - instance_p = jsonutils.to_primitive(instance) + if self.can_send_version('2.38'): + version = '2.38' + else: + version = '2.0' + instance = jsonutils.to_primitive( + objects_base.obj_to_primitive(instance)) return self.call(ctxt, self.make_msg('check_can_live_migrate_destination', - instance=instance_p, + instance=instance, block_migration=block_migration, disk_over_commit=disk_over_commit), topic=_compute_topic(self.topic, - ctxt, destination, None)) + ctxt, destination, None), + version=version) def check_can_live_migrate_source(self, ctxt, instance, dest_check_data): - instance_p = jsonutils.to_primitive(instance) + if self.can_send_version('2.38'): + version = '2.38' + else: + version = '2.0' + instance = jsonutils.to_primitive( + objects_base.obj_to_primitive(instance)) return self.call(ctxt, self.make_msg('check_can_live_migrate_source', - instance=instance_p, + instance=instance, dest_check_data=dest_check_data), topic=_compute_topic(self.topic, ctxt, None, - instance)) + instance), + version=version) def check_instance_shared_storage(self, ctxt, instance, data): instance_p = jsonutils.to_primitive(instance) diff --git a/nova/conductor/api.py b/nova/conductor/api.py index df9772cf35c7..69198fdec517 100644 --- a/nova/conductor/api.py +++ b/nova/conductor/api.py @@ -349,12 +349,21 @@ class LocalComputeTaskAPI(object): self._manager = utils.ExceptionHelper( manager.ComputeTaskManager()) - def migrate_server(self, context, instance, scheduler_hint, live, rebuild, - flavor, block_migration, disk_over_commit, - reservations=None): + def resize_instance(self, context, instance, extra_instance_updates, + scheduler_hint, flavor, reservations): + # NOTE(comstud): 'extra_instance_updates' is not used here but is + # needed for compatibility with the cells_rpcapi version of this + # method. self._manager.migrate_server( - context, instance, scheduler_hint, live, rebuild, flavor, - block_migration, disk_over_commit, reservations) + context, instance, scheduler_hint, False, False, flavor, + None, None, reservations) + + def live_migrate_instance(self, context, instance, host_name, + block_migration, disk_over_commit): + scheduler_hint = {'host': host_name} + self._manager.migrate_server( + context, instance, scheduler_hint, True, False, None, + block_migration, disk_over_commit, None) def build_instances(self, context, instances, image, filter_properties, admin_password, injected_files, @@ -423,12 +432,21 @@ class ComputeTaskAPI(object): def __init__(self): self.conductor_compute_rpcapi = rpcapi.ComputeTaskAPI() - def migrate_server(self, context, instance, scheduler_hint, live, rebuild, - flavor, block_migration, disk_over_commit, - reservations=None): + def resize_instance(self, context, instance, extra_instance_updates, + scheduler_hint, flavor, reservations): + # NOTE(comstud): 'extra_instance_updates' is not used here but is + # needed for compatibility with the cells_rpcapi version of this + # method. self.conductor_compute_rpcapi.migrate_server( - context, instance, scheduler_hint, live, rebuild, - flavor, block_migration, disk_over_commit, reservations) + context, instance, scheduler_hint, False, False, flavor, + None, None, reservations) + + def live_migrate_instance(self, context, instance, host_name, + block_migration, disk_over_commit): + scheduler_hint = {'host': host_name} + self.conductor_compute_rpcapi.migrate_server( + context, instance, scheduler_hint, True, False, None, + block_migration, disk_over_commit, None) def build_instances(self, context, instances, image, filter_properties, admin_password, injected_files, requested_networks, diff --git a/nova/conductor/manager.py b/nova/conductor/manager.py index d85df20bf7a2..3cbd831e3782 100644 --- a/nova/conductor/manager.py +++ b/nova/conductor/manager.py @@ -582,7 +582,7 @@ class ComputeTaskManager(base.Base): """ RPC_API_NAMESPACE = 'compute_task' - RPC_API_VERSION = '1.5' + RPC_API_VERSION = '1.6' def __init__(self): super(ComputeTaskManager, self).__init__() @@ -601,6 +601,14 @@ class ComputeTaskManager(base.Base): exception.MigrationPreCheckError) def migrate_server(self, context, instance, scheduler_hint, live, rebuild, flavor, block_migration, disk_over_commit, reservations=None): + if instance and not isinstance(instance, instance_obj.Instance): + # NOTE(danms): Until v2 of the RPC API, we need to tolerate + # old-world instance objects here + attrs = ['metadata', 'system_metadata', 'info_cache', + 'security_groups'] + instance = instance_obj.Instance._from_db_object( + context, instance_obj.Instance(), instance, + expected_attrs=attrs) if live and not rebuild and not flavor: self._live_migrate(context, instance, scheduler_hint, block_migration, disk_over_commit) @@ -616,7 +624,7 @@ class ComputeTaskManager(base.Base): def _cold_migrate(self, context, instance, flavor, filter_properties, reservations): - image_ref = instance.get('image_ref') + image_ref = instance.image_ref if image_ref: image = self._get_image(context, image_ref) else: @@ -655,7 +663,8 @@ class ComputeTaskManager(base.Base): (host, node) = (host_state['host'], host_state['nodename']) self.compute_rpcapi.prep_resize( - context, image, instance, flavor, host, + context, image, nova_object.obj_to_primitive(instance), + flavor, host, reservations, request_spec=request_spec, filter_properties=filter_properties, node=node) except Exception as ex: @@ -722,12 +731,6 @@ class ComputeTaskManager(base.Base): filter_properties=filter_properties, legacy_bdm_in_spec=legacy_bdm) - def _instance_update(self, context, instance_uuid, **kwargs): - (old_ref, instance_ref) = self.db.instance_update_and_get_original( - context, instance_uuid, kwargs) - notifications.send_update(context, old_ref, instance_ref, 'conductor') - return instance_ref - def _get_image(self, context, image_id): if not image_id: return None diff --git a/nova/conductor/rpcapi.py b/nova/conductor/rpcapi.py index 52598a81541b..103585912051 100644 --- a/nova/conductor/rpcapi.py +++ b/nova/conductor/rpcapi.py @@ -519,6 +519,7 @@ class ComputeTaskAPI(nova.openstack.common.rpc.proxy.RpcProxy): 1.3 - Added unshelve_instance 1.4 - Added reservations to migrate_server. 1.5 - Added the leagacy_bdm parameter to build_instances + 1.6 - Made migrate_server use instance objects """ BASE_RPC_API_VERSION = '1.0' @@ -533,13 +534,18 @@ class ComputeTaskAPI(nova.openstack.common.rpc.proxy.RpcProxy): def migrate_server(self, context, instance, scheduler_hint, live, rebuild, flavor, block_migration, disk_over_commit, reservations=None): - instance_p = jsonutils.to_primitive(instance) + if self.can_send_version('1.6'): + version = '1.6' + else: + instance = jsonutils.to_primitive( + objects_base.obj_to_primitive(instance)) + version = '1.4' flavor_p = jsonutils.to_primitive(flavor) - msg = self.make_msg('migrate_server', instance=instance_p, + msg = self.make_msg('migrate_server', instance=instance, scheduler_hint=scheduler_hint, live=live, rebuild=rebuild, flavor=flavor_p, block_migration=block_migration, disk_over_commit=disk_over_commit, reservations=reservations) - return self.call(context, msg, version='1.4') + return self.call(context, msg, version=version) def build_instances(self, context, instances, image, filter_properties, admin_password, injected_files, requested_networks, diff --git a/nova/conductor/tasks/live_migrate.py b/nova/conductor/tasks/live_migrate.py index 3d47b6cbddbf..1d0c7089531e 100644 --- a/nova/conductor/tasks/live_migrate.py +++ b/nova/conductor/tasks/live_migrate.py @@ -20,6 +20,7 @@ from nova.compute import rpcapi as compute_rpcapi from nova import db from nova import exception from nova.image import glance +from nova.objects import base as obj_base from nova.openstack.common.gettextutils import _ from nova.openstack.common import log as logging from nova.scheduler import rpcapi as scheduler_rpcapi @@ -45,7 +46,7 @@ class LiveMigrationTask(object): self.destination = destination self.block_migration = block_migration self.disk_over_commit = disk_over_commit - self.source = instance['host'] + self.source = instance.host self.migrate_data = None self.compute_rpcapi = compute_rpcapi.ComputeAPI() self.servicegroup_api = servicegroup.API() @@ -79,9 +80,9 @@ class LiveMigrationTask(object): raise NotImplementedError() def _check_instance_is_running(self): - if self.instance['power_state'] != power_state.RUNNING: + if self.instance.power_state != power_state.RUNNING: raise exception.InstanceNotRunning( - instance_id=self.instance['uuid']) + instance_id=self.instance.uuid) def _check_host_is_up(self, host): try: @@ -102,14 +103,14 @@ class LiveMigrationTask(object): def _check_destination_is_not_source(self): if self.destination == self.source: raise exception.UnableToMigrateToSelf( - instance_id=self.instance['uuid'], host=self.destination) + instance_id=self.instance.uuid, host=self.destination) def _check_destination_has_enough_memory(self): avail = self._get_compute_info(self.destination)['free_ram_mb'] - mem_inst = self.instance['memory_mb'] + mem_inst = self.instance.memory_mb if not mem_inst or avail <= mem_inst: - instance_uuid = self.instance['uuid'] + instance_uuid = self.instance.uuid dest = self.destination reason = _("Unable to migrate %(instance_uuid)s to %(dest)s: " "Lack of memory(host:%(avail)s <= " @@ -145,9 +146,9 @@ class LiveMigrationTask(object): #TODO(johngarbutt) this retry loop should be shared attempted_hosts = [self.source] image = None - if self.instance['image_ref']: + if self.instance.image_ref: image = self.image_service.show(self.context, - self.instance['image_ref']) + self.instance.image_ref) instance_type = flavors.extract_flavor(self.instance) host = None @@ -168,9 +169,10 @@ class LiveMigrationTask(object): def _get_candidate_destination(self, image, instance_type, attempted_hosts): - request_spec = {'instance_properties': self.instance, + instance_p = obj_base.obj_to_primitive(self.instance) + request_spec = {'instance_properties': instance_p, 'instance_type': instance_type, - 'instance_uuids': [self.instance['uuid']]} + 'instance_uuids': [self.instance.uuid]} if image: request_spec['image'] = image filter_properties = {'ignore_hosts': attempted_hosts} @@ -186,7 +188,7 @@ class LiveMigrationTask(object): msg = (_('Exceeded max scheduling retries %(max_retries)d for ' 'instance %(instance_uuid)s during live migration') % {'max_retries': retries, - 'instance_uuid': self.instance['uuid']}) + 'instance_uuid': self.instance.uuid}) raise exception.NoValidHost(reason=msg) diff --git a/nova/tests/api/openstack/compute/contrib/test_admin_actions.py b/nova/tests/api/openstack/compute/contrib/test_admin_actions.py index 47b0b4f81ce0..4798f57e479f 100644 --- a/nova/tests/api/openstack/compute/contrib/test_admin_actions.py +++ b/nova/tests/api/openstack/compute/contrib/test_admin_actions.py @@ -153,7 +153,7 @@ class AdminActionsTest(CommonMixin, test.TestCase): actions = ['pause', 'unpause', 'suspend', 'resume', 'migrate', 'resetNetwork', 'injectNetworkInfo', 'lock', 'unlock'] - actions_not_objectified = ['migrate', 'resetNetwork', 'lock', + actions_not_objectified = ['resetNetwork', 'lock', 'unlock', 'injectNetworkInfo'] method_translations = {'migrate': 'resize', 'resetNetwork': 'reset_network', @@ -169,15 +169,12 @@ class AdminActionsTest(CommonMixin, test.TestCase): def test_actions_raise_conflict_on_invalid_state(self): actions = ['pause', 'unpause', 'suspend', 'resume', 'migrate'] - actions_not_objectified = ['migrate'] method_translations = {'migrate': 'resize'} for action in actions: - old_style = action in actions_not_objectified method = method_translations.get(action) self.mox.StubOutWithMock(self.compute_api, method or action) - self._test_invalid_state(action, method=method, - objects=not old_style) + self._test_invalid_state(action, method=method) # Re-mock this. self.mox.StubOutWithMock(self.compute_api, 'get') @@ -198,7 +195,7 @@ class AdminActionsTest(CommonMixin, test.TestCase): def _test_migrate_exception(self, exc_info, expected_result): self.mox.StubOutWithMock(self.compute_api, 'resize') - instance = self._stub_instance_get(objects=False) + instance = self._stub_instance_get() self.compute_api.resize(self.context, instance).AndRaise(exc_info) self.mox.ReplayAll() @@ -209,7 +206,7 @@ class AdminActionsTest(CommonMixin, test.TestCase): def test_migrate_live_enabled(self): self.mox.StubOutWithMock(self.compute_api, 'live_migrate') - instance = self._stub_instance_get(objects=False) + instance = self._stub_instance_get() self.compute_api.live_migrate(self.context, instance, False, False, 'hostname') @@ -233,13 +230,13 @@ class AdminActionsTest(CommonMixin, test.TestCase): uuid=None): self.mox.StubOutWithMock(self.compute_api, 'live_migrate') - instance = self._stub_instance_get(uuid=uuid, objects=False) + instance = self._stub_instance_get(uuid=uuid) self.compute_api.live_migrate(self.context, instance, False, False, 'hostname').AndRaise(fake_exc) self.mox.ReplayAll() - res = self._make_request('/servers/%s/action' % instance['uuid'], + res = self._make_request('/servers/%s/action' % instance.uuid, {'os-migrateLive': {'host': 'hostname', 'block_migration': False, diff --git a/nova/tests/api/openstack/compute/plugins/v3/test_admin_actions.py b/nova/tests/api/openstack/compute/plugins/v3/test_admin_actions.py index c04507bcb361..5f94bcfa76fc 100644 --- a/nova/tests/api/openstack/compute/plugins/v3/test_admin_actions.py +++ b/nova/tests/api/openstack/compute/plugins/v3/test_admin_actions.py @@ -167,8 +167,8 @@ class AdminActionsTest(CommonMixin, test.TestCase): actions = ['pause', 'unpause', 'suspend', 'resume', 'migrate', 'reset_network', 'inject_network_info', 'lock', 'unlock'] - actions_not_objectified = ['migrate', 'reset_network', 'lock', - 'unlock', 'inject_network_info'] + actions_not_objectified = ['reset_network', 'lock', 'unlock', + 'inject_network_info'] method_translations = {'migrate': 'resize'} for action in actions: @@ -182,7 +182,6 @@ class AdminActionsTest(CommonMixin, test.TestCase): def test_actions_raise_conflict_on_invalid_state(self): actions = ['pause', 'unpause', 'suspend', 'resume', 'migrate', 'migrate_live'] - actions_not_objectified = ['migrate', 'migrate_live'] method_translations = {'migrate': 'resize', 'migrate_live': 'live_migrate'} @@ -192,13 +191,11 @@ class AdminActionsTest(CommonMixin, test.TestCase): args_map = {'migrate_live': ((False, False, 'hostname'), {})} for action in actions: - old_style = action in actions_not_objectified method = method_translations.get(action) self.mox.StubOutWithMock(self.compute_api, method or action) self._test_invalid_state(action, method=method, body_map=body_map, - compute_api_args_map=args_map, - objects=not old_style) + compute_api_args_map=args_map) # Re-mock this. self.mox.StubOutWithMock(self.compute_api, 'get') @@ -206,9 +203,8 @@ class AdminActionsTest(CommonMixin, test.TestCase): actions = ['pause', 'unpause', 'suspend', 'resume', 'migrate', 'reset_network', 'inject_network_info', 'lock', 'unlock', 'reset_state', 'migrate_live'] - actions_not_objectified = ['migrate', 'reset_network', 'lock', - 'unlock', 'inject_network_info', - 'migrate_live'] + actions_not_objectified = ['reset_network', 'lock', 'unlock', + 'inject_network_info'] body_map = {'reset_state': {'state': 'active'}, 'migrate_live': {'host': 'hostname', 'block_migration': False, @@ -225,8 +221,7 @@ class AdminActionsTest(CommonMixin, test.TestCase): actions = ['pause', 'unpause', 'suspend', 'resume', 'migrate', 'reset_network', 'inject_network_info'] method_translations = {'migrate': 'resize'} - actions_not_objectified = ['migrate', 'reset_network', - 'inject_network_info'] + actions_not_objectified = ['reset_network', 'inject_network_info'] for action in actions: old_style = action in actions_not_objectified @@ -239,7 +234,7 @@ class AdminActionsTest(CommonMixin, test.TestCase): def _test_migrate_exception(self, exc_info, expected_result): self.mox.StubOutWithMock(self.compute_api, 'resize') - instance = self._stub_instance_get(objects=False) + instance = self._stub_instance_get() self.compute_api.resize(self.context, instance).AndRaise(exc_info) self.mox.ReplayAll() @@ -259,13 +254,13 @@ class AdminActionsTest(CommonMixin, test.TestCase): def test_migrate_live_enabled(self): self.mox.StubOutWithMock(self.compute_api, 'live_migrate') - instance = self._stub_instance_get(objects=False) + instance = self._stub_instance_get() self.compute_api.live_migrate(self.context, instance, False, False, 'hostname') self.mox.ReplayAll() - res = self._make_request('/servers/%s/action' % instance['uuid'], + res = self._make_request('/servers/%s/action' % instance.uuid, {'migrate_live': {'host': 'hostname', 'block_migration': False, @@ -283,13 +278,13 @@ class AdminActionsTest(CommonMixin, test.TestCase): uuid=None): self.mox.StubOutWithMock(self.compute_api, 'live_migrate') - instance = self._stub_instance_get(uuid=uuid, objects=False) + instance = self._stub_instance_get(uuid=uuid) self.compute_api.live_migrate(self.context, instance, False, False, 'hostname').AndRaise(fake_exc) self.mox.ReplayAll() - res = self._make_request('/servers/%s/action' % instance['uuid'], + res = self._make_request('/servers/%s/action' % instance.uuid, {'migrate_live': {'host': 'hostname', 'block_migration': False, diff --git a/nova/tests/cells/test_cells_manager.py b/nova/tests/cells/test_cells_manager.py index 83e8c60bf755..eb1bd45fff5a 100644 --- a/nova/tests/cells/test_cells_manager.py +++ b/nova/tests/cells/test_cells_manager.py @@ -732,3 +732,23 @@ class CellsManagerClassTestCase(test.TestCase): self.mox.ReplayAll() self.cells_manager.soft_delete_instance(self.ctxt, instance='fake-instance') + + def test_resize_instance(self): + self.mox.StubOutWithMock(self.msg_runner, 'resize_instance') + self.msg_runner.resize_instance(self.ctxt, 'fake-instance', + 'fake-flavor', 'fake-updates') + self.mox.ReplayAll() + self.cells_manager.resize_instance( + self.ctxt, instance='fake-instance', flavor='fake-flavor', + extra_instance_updates='fake-updates') + + def test_live_migrate_instance(self): + self.mox.StubOutWithMock(self.msg_runner, 'live_migrate_instance') + self.msg_runner.live_migrate_instance(self.ctxt, 'fake-instance', + 'fake-block', 'fake-commit', + 'fake-host') + self.mox.ReplayAll() + self.cells_manager.live_migrate_instance( + self.ctxt, instance='fake-instance', + block_migration='fake-block', disk_over_commit='fake-commit', + host_name='fake-host') diff --git a/nova/tests/cells/test_cells_messaging.py b/nova/tests/cells/test_cells_messaging.py index 9553779e7a91..be3722e304f1 100644 --- a/nova/tests/cells/test_cells_messaging.py +++ b/nova/tests/cells/test_cells_messaging.py @@ -1237,6 +1237,22 @@ class CellsTargetedMethodsTestCase(test.TestCase): def test_unpause_instance(self): self._test_instance_action_method('unpause', (), {}, (), {}, False) + def test_resize_instance(self): + kwargs = dict(flavor=dict(id=42), + extra_instance_updates=dict(cow='moo')) + expected_kwargs = dict(flavor_id=42, cow='moo') + self._test_instance_action_method('resize', (), kwargs, + (), expected_kwargs, + False) + + def test_live_migrate_instance(self): + kwargs = dict(block_migration='fake-block-mig', + disk_over_commit='fake-commit', + host_name='fake-host') + expected_args = ('fake-block-mig', 'fake-commit', 'fake-host') + self._test_instance_action_method('live_migrate', (), kwargs, + expected_args, {}, False) + class CellsBroadcastMethodsTestCase(test.TestCase): """Test case for _BroadcastMessageMethods class. Most of these diff --git a/nova/tests/cells/test_cells_rpcapi.py b/nova/tests/cells/test_cells_rpcapi.py index 4e5498cebb32..683544ae8a9d 100644 --- a/nova/tests/cells/test_cells_rpcapi.py +++ b/nova/tests/cells/test_cells_rpcapi.py @@ -634,3 +634,33 @@ class CellsAPITestCase(test.TestCase): expected_args = {'instance': 'fake-instance'} self._check_result(call_info, 'soft_delete_instance', expected_args, version='1.18') + + def test_resize_instance(self): + call_info = self._stub_rpc_method('cast', None) + + self.cells_rpcapi.resize_instance(self.fake_context, + 'fake-instance', + dict(cow='moo'), + 'fake-hint', + 'fake-flavor', + 'fake-reservations') + expected_args = {'instance': 'fake-instance', + 'flavor': 'fake-flavor', + 'extra_instance_updates': dict(cow='moo')} + self._check_result(call_info, 'resize_instance', + expected_args, version='1.20') + + def test_live_migrate_instance(self): + call_info = self._stub_rpc_method('cast', None) + + self.cells_rpcapi.live_migrate_instance(self.fake_context, + 'fake-instance', + 'fake-host', + 'fake-block', + 'fake-commit') + expected_args = {'instance': 'fake-instance', + 'block_migration': 'fake-block', + 'disk_over_commit': 'fake-commit', + 'host_name': 'fake-host'} + self._check_result(call_info, 'live_migrate_instance', + expected_args, version='1.20') diff --git a/nova/tests/compute/test_compute.py b/nova/tests/compute/test_compute.py index c185ef765180..62c8994c1ce7 100644 --- a/nova/tests/compute/test_compute.py +++ b/nova/tests/compute/test_compute.py @@ -4012,123 +4012,6 @@ class ComputeTestCase(BaseTestCase): self.compute.terminate_instance(self.context, instance=jsonutils.to_primitive(inst_ref)) - def test_check_can_live_migrate_source_works_correctly(self): - # Confirm check_can_live_migrate_source works on positive path. - def fake_method(*args, **kwargs): - return {} - self.stubs.Set(self.compute.driver, 'check_can_live_migrate_source', - fake_method) - inst_ref = jsonutils.to_primitive(self._create_fake_instance( - {'host': 'fake_host_2'})) - - self.mox.StubOutWithMock(db, 'instance_get') - dest_check_data = {"test": "data"} - - self.mox.ReplayAll() - ret = self.compute.check_can_live_migrate_source(self.context, - dest_check_data=dest_check_data, - instance=inst_ref) - self.assertTrue(type(ret) == dict) - - def test_check_can_live_migrate_destination_works_correctly(self): - # Confirm check_can_live_migrate_destination works on positive path. - def fake_method(*args, **kwargs): - return {} - self.stubs.Set(self.compute.compute_rpcapi, - 'check_can_live_migrate_source', - fake_method) - inst_ref = jsonutils.to_primitive(self._create_fake_instance( - {'host': 'fake_host_2'})) - compute_info = {"compute": "info"} - - self.mox.StubOutWithMock(self.compute, - '_get_compute_info') - self.mox.StubOutWithMock(self.compute.driver, - 'check_can_live_migrate_destination') - self.mox.StubOutWithMock(self.compute.driver, - 'check_can_live_migrate_destination_cleanup') - - dest_check_data = {"test": "data", "migrate_data": {"test": "data"}} - self.compute._get_compute_info( - self.context, inst_ref['host']).AndReturn(compute_info) - self.compute._get_compute_info( - self.context, CONF.host).AndReturn(compute_info) - self.compute.driver.check_can_live_migrate_destination(self.context, - inst_ref, - compute_info, compute_info, - True, False).AndReturn(dest_check_data) - self.compute.compute_rpcapi.check_can_live_migrate_source(self.context, - inst_ref, dest_check_data) - self.compute.driver.check_can_live_migrate_destination_cleanup( - self.context, dest_check_data) - - self.mox.ReplayAll() - ret = self.compute.check_can_live_migrate_destination(self.context, - block_migration=True, disk_over_commit=False, - instance=inst_ref) - self.assertTrue(type(ret) == dict) - self.assertTrue("test" in ret) - - def test_check_can_live_migrate_destination_fails_dest_check(self): - inst_ref = jsonutils.to_primitive(self._create_fake_instance( - {'host': 'fake_host_2'})) - compute_info = {"compute": "info"} - - self.mox.StubOutWithMock(self.compute, - '_get_compute_info') - self.mox.StubOutWithMock(self.compute.driver, - 'check_can_live_migrate_destination') - - self.compute._get_compute_info( - self.context, inst_ref['host']).AndReturn(compute_info) - self.compute._get_compute_info( - self.context, CONF.host).AndReturn(compute_info) - self.compute.driver.check_can_live_migrate_destination(self.context, - inst_ref, - compute_info, compute_info, - True, False).AndRaise(exception.Invalid()) - - self.mox.ReplayAll() - self.assertRaises(exception.Invalid, - self.compute.check_can_live_migrate_destination, - self.context, block_migration=True, - disk_over_commit=False, instance=inst_ref) - - def test_check_can_live_migrate_destination_fails_source(self): - # Confirm check_can_live_migrate_destination works on positive path. - inst_ref = jsonutils.to_primitive(self._create_fake_instance( - {'host': 'fake_host_2'})) - compute_info = {"compute": "info"} - - self.mox.StubOutWithMock(self.compute, - '_get_compute_info') - self.mox.StubOutWithMock(self.compute.driver, - 'check_can_live_migrate_destination') - self.mox.StubOutWithMock(self.compute.compute_rpcapi, - 'check_can_live_migrate_source') - self.mox.StubOutWithMock(self.compute.driver, - 'check_can_live_migrate_destination_cleanup') - - dest_check_data = {"test": "data"} - self.compute._get_compute_info( - self.context, inst_ref['host']).AndReturn(compute_info) - self.compute._get_compute_info( - self.context, CONF.host).AndReturn(compute_info) - self.compute.driver.check_can_live_migrate_destination(self.context, - inst_ref, - compute_info, compute_info, - True, False).AndReturn(dest_check_data) - self.compute.compute_rpcapi.check_can_live_migrate_source(self.context, - inst_ref, dest_check_data).AndRaise(exception.Invalid()) - self.compute.driver.check_can_live_migrate_destination_cleanup( - self.context, dest_check_data) - - self.mox.ReplayAll() - self.assertRaises(exception.Invalid, - self.compute.check_can_live_migrate_destination, - self.context, block_migration=True, - disk_over_commit=False, instance=inst_ref) - def test_pre_live_migration_instance_has_no_fixed_ip(self): # Confirm raising exception if instance doesn't have fixed_ip. # creating instance testdata @@ -7908,28 +7791,24 @@ class ComputeAPITestCase(BaseTestCase): def test_live_migrate(self): instance, instance_uuid = self._run_instance() + instance = self._objectify(instance) - def fake_live_migrate(_self, context, instance, scheduler_hint, - block_migration, disk_over_commit): - host = scheduler_hint["host"] - self.assertEqual('fake_dest_host', host) - self.assertTrue(block_migration) - self.assertTrue(disk_over_commit) + rpcapi = self.compute_api.compute_task_api + self.mox.StubOutWithMock(rpcapi, 'live_migrate_instance') + rpcapi.live_migrate_instance(self.context, instance, 'fake_dest_host', + block_migration=True, + disk_over_commit=True) - self.stubs.Set(conductor_manager.ComputeTaskManager, - '_live_migrate', - fake_live_migrate) + self.mox.ReplayAll() self.compute_api.live_migrate(self.context, instance, block_migration=True, disk_over_commit=True, host_name='fake_dest_host') - instance = db.instance_get_by_uuid(self.context, instance_uuid) + instance.refresh() self.assertEqual(instance['task_state'], task_states.MIGRATING) - db.instance_destroy(self.context, instance['uuid']) - def test_evacuate(self): instance = jsonutils.to_primitive(self._create_fake_instance( services=True)) @@ -8596,7 +8475,7 @@ class DisabledInstanceTypesTestCase(BaseTestCase): self.compute_api.create, self.context, self.inst_type, None) def test_can_resize_to_visible_instance_type(self): - instance = self._create_fake_instance() + instance = self._create_fake_instance_obj() orig_get_flavor_by_flavor_id =\ flavors.get_flavor_by_flavor_id @@ -8618,7 +8497,7 @@ class DisabledInstanceTypesTestCase(BaseTestCase): self.compute_api.resize(self.context, instance, '4') def test_cannot_resize_to_disabled_instance_type(self): - instance = self._create_fake_instance() + instance = self._create_fake_instance_obj() orig_get_flavor_by_flavor_id = \ flavors.get_flavor_by_flavor_id diff --git a/nova/tests/compute/test_compute_api.py b/nova/tests/compute/test_compute_api.py index e2e5ef277cae..43de11c963fa 100644 --- a/nova/tests/compute/test_compute_api.py +++ b/nova/tests/compute/test_compute_api.py @@ -30,6 +30,7 @@ from nova import exception from nova.objects import base as obj_base from nova.objects import instance as instance_obj from nova.objects import instance_info_cache +from nova.objects import migration as migration_obj from nova.openstack.common import timeutils from nova.openstack.common import uuidutils from nova import quota @@ -629,8 +630,7 @@ class _ComputeAPIUnitTestMixIn(object): def _test_revert_resize(self): params = dict(vm_state=vm_states.RESIZED) - fake_inst = obj_base.obj_to_primitive( - self._create_instance_obj(params=params)) + fake_inst = self._create_instance_obj(params=params) fake_mig = dict(id='fake-migration-id', dest_compute='dest_host') @@ -640,7 +640,7 @@ class _ComputeAPIUnitTestMixIn(object): self.mox.StubOutWithMock(self.compute_api, '_reverse_upsize_quota_delta') self.mox.StubOutWithMock(self.compute_api, '_reserve_quota_delta') - self.mox.StubOutWithMock(self.compute_api, 'update') + self.mox.StubOutWithMock(fake_inst, 'save') self.mox.StubOutWithMock(self.compute_api.db, 'migration_update') self.mox.StubOutWithMock(quota.QUOTAS, 'commit') self.mox.StubOutWithMock(self.compute_api, '_record_action_start') @@ -658,10 +658,14 @@ class _ComputeAPIUnitTestMixIn(object): self.compute_api._reserve_quota_delta(self.context, 'deltas').AndReturn(resvs) - self.compute_api.update( - self.context, fake_inst, - task_state=task_states.RESIZE_REVERTING, - expected_task_state=None).AndReturn(fake_inst) + + def _check_state(expected_task_state=None): + self.assertEqual(task_states.RESIZE_REVERTING, + fake_inst.task_state) + + fake_inst.save(expected_task_state=None).WithSideEffects( + _check_state) + self.compute_api.db.migration_update(self.context, 'fake-migration-id', {'status': 'reverting'}) @@ -705,17 +709,16 @@ class _ComputeAPIUnitTestMixIn(object): if project_id is not None: # To test instance w/ different project id than context (admin) params['project_id'] = project_id - fake_inst = obj_base.obj_to_primitive( - self._create_instance_obj(params=params)) + fake_inst = self._create_instance_obj(params=params) self.mox.StubOutWithMock(flavors, 'get_flavor_by_flavor_id') self.mox.StubOutWithMock(self.compute_api, '_upsize_quota_delta') self.mox.StubOutWithMock(self.compute_api, '_reserve_quota_delta') - self.mox.StubOutWithMock(self.compute_api, 'update') + self.mox.StubOutWithMock(fake_inst, 'save') self.mox.StubOutWithMock(quota.QUOTAS, 'commit') self.mox.StubOutWithMock(self.compute_api, '_record_action_start') self.mox.StubOutWithMock(self.compute_api.compute_task_api, - 'migrate_server') + 'resize_instance') current_flavor = flavors.extract_flavor(fake_inst) if flavor_id_passed: @@ -734,10 +737,15 @@ class _ComputeAPIUnitTestMixIn(object): current_flavor).AndReturn('deltas') self.compute_api._reserve_quota_delta(self.context, 'deltas', project_id=fake_inst['project_id']).AndReturn(resvs) - self.compute_api.update(self.context, fake_inst, - task_state=task_states.RESIZE_PREP, - expected_task_state=None, - progress=0, **extra_kwargs).AndReturn(fake_inst) + + def _check_state(expected_task_state=None): + self.assertEqual(task_states.RESIZE_PREP, fake_inst.task_state) + self.assertEqual(fake_inst.progress, 0) + for key, value in extra_kwargs.items(): + self.assertEqual(value, getattr(fake_inst, key)) + + fake_inst.save(expected_task_state=None).WithSideEffects( + _check_state) if allow_same_host: filter_properties = {'ignore_hosts': []} @@ -751,36 +759,35 @@ class _ComputeAPIUnitTestMixIn(object): quota.QUOTAS.commit(self.context, resvs, project_id=fake_inst['project_id']) resvs = [] + mig = migration_obj.Migration() + + def _get_migration(): + return mig + + def _check_mig(ctxt): + self.assertEqual(fake_inst.uuid, mig.instance_uuid) + self.assertEqual(current_flavor['id'], + mig.old_instance_type_id) + self.assertEqual(new_flavor['id'], + mig.new_instance_type_id) + self.assertEqual('finished', mig.status) + + self.stubs.Set(migration_obj, 'Migration', _get_migration) + self.mox.StubOutWithMock(self.context, 'elevated') + self.mox.StubOutWithMock(mig, 'create') + + self.context.elevated().AndReturn(self.context) + mig.create(self.context).WithSideEffects(_check_mig) self.compute_api._record_action_start(self.context, fake_inst, 'resize') scheduler_hint = {'filter_properties': filter_properties} - self.compute_api.compute_task_api.migrate_server( - self.context, fake_inst, scheduler_hint=scheduler_hint, - live=False, rebuild=False, flavor=new_flavor, - block_migration=None, disk_over_commit=None, - reservations=resvs) - - if self.is_cells: - self.mox.StubOutWithMock(self.context, 'elevated') - self.mox.StubOutWithMock(self.compute_api.db, 'migration_create') - self.mox.StubOutWithMock(self.compute_api, '_cast_to_cells') - if flavor_id_passed: - flavors.get_flavor_by_flavor_id( - 'new-flavor-id', - read_deleted='no').AndReturn(new_flavor) - self.context.elevated().AndReturn(self.context) - self.compute_api.db.migration_create( - self.context, - dict(instance_uuid=fake_inst['uuid'], - old_instance_type_id=current_flavor['id'], - new_instance_type_id=new_flavor['id'], - status='finished')) - self.compute_api._cast_to_cells( - self.context, fake_inst, 'resize', - flavor_id=new_flavor['flavorid'], **extra_kwargs) + self.compute_api.compute_task_api.resize_instance( + self.context, fake_inst, extra_kwargs, + scheduler_hint=scheduler_hint, + flavor=new_flavor, reservations=resvs) self.mox.ReplayAll() @@ -832,7 +839,7 @@ class _ComputeAPIUnitTestMixIn(object): self.mox.StubOutWithMock(quota.QUOTAS, 'commit') self.mox.StubOutWithMock(self.compute_api, '_record_action_start') self.mox.StubOutWithMock(self.compute_api.compute_task_api, - 'migrate_server') + 'resize_instance') fake_inst = obj_base.obj_to_primitive(self._create_instance_obj()) exc = exception.FlavorNotFound(flavor_id='flavor-id') @@ -854,7 +861,7 @@ class _ComputeAPIUnitTestMixIn(object): self.mox.StubOutWithMock(quota.QUOTAS, 'commit') self.mox.StubOutWithMock(self.compute_api, '_record_action_start') self.mox.StubOutWithMock(self.compute_api.compute_task_api, - 'migrate_server') + 'resize_instance') fake_inst = obj_base.obj_to_primitive(self._create_instance_obj()) fake_flavor = dict(id=200, flavorid='flavor-id', name='foo', @@ -877,7 +884,7 @@ class _ComputeAPIUnitTestMixIn(object): self.mox.StubOutWithMock(quota.QUOTAS, 'commit') self.mox.StubOutWithMock(self.compute_api, '_record_action_start') self.mox.StubOutWithMock(self.compute_api.compute_task_api, - 'migrate_server') + 'resize_instance') fake_inst = obj_base.obj_to_primitive(self._create_instance_obj()) fake_flavor = flavors.extract_flavor(fake_inst) @@ -902,7 +909,7 @@ class _ComputeAPIUnitTestMixIn(object): self.mox.StubOutWithMock(quota.QUOTAS, 'commit') self.mox.StubOutWithMock(self.compute_api, '_record_action_start') self.mox.StubOutWithMock(self.compute_api.compute_task_api, - 'migrate_server') + 'resize_instance') fake_inst = obj_base.obj_to_primitive(self._create_instance_obj()) current_flavor = flavors.extract_flavor(fake_inst) diff --git a/nova/tests/compute/test_compute_mgr.py b/nova/tests/compute/test_compute_mgr.py index e7f09accca5b..687aa255ca7c 100644 --- a/nova/tests/compute/test_compute_mgr.py +++ b/nova/tests/compute/test_compute_mgr.py @@ -25,6 +25,7 @@ from nova import context from nova import db from nova import exception from nova.network import model as network_model +from nova.objects import base as obj_base from nova.objects import instance as instance_obj from nova.openstack.common import importutils from nova.openstack.common import uuidutils @@ -627,6 +628,99 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase): self.assertTrue(volumes[old_volume_id]['status'], 'detaching') self.assertTrue(volumes[new_volume_id]['status'], 'attaching') + def test_check_can_live_migrate_source(self): + is_volume_backed = 'volume_backed' + bdms = 'bdms' + dest_check_data = dict(foo='bar') + db_instance = fake_instance.fake_db_instance() + instance = instance_obj.Instance._from_db_object( + self.context, instance_obj.Instance(), db_instance) + expected_dest_check_data = dict(dest_check_data, + is_volume_backed=is_volume_backed) + + self.mox.StubOutWithMock(self.compute.conductor_api, + 'block_device_mapping_get_all_by_instance') + self.mox.StubOutWithMock(self.compute.compute_api, + 'is_volume_backed_instance') + self.mox.StubOutWithMock(self.compute.driver, + 'check_can_live_migrate_source') + + instance_p = obj_base.obj_to_primitive(instance) + self.compute.conductor_api.block_device_mapping_get_all_by_instance( + self.context, instance_p).AndReturn(bdms) + self.compute.compute_api.is_volume_backed_instance( + self.context, instance, bdms).AndReturn(is_volume_backed) + self.compute.driver.check_can_live_migrate_source( + self.context, instance, expected_dest_check_data) + + self.mox.ReplayAll() + + self.compute.check_can_live_migrate_source( + self.context, instance=instance, + dest_check_data=dest_check_data) + + def _test_check_can_live_migrate_destination(self, do_raise=False, + has_mig_data=False): + db_instance = fake_instance.fake_db_instance(host='fake-host') + instance = instance_obj.Instance._from_db_object( + self.context, instance_obj.Instance(), db_instance) + instance.host = 'fake-host' + block_migration = 'block_migration' + disk_over_commit = 'disk_over_commit' + src_info = 'src_info' + dest_info = 'dest_info' + dest_check_data = dict(foo='bar') + mig_data = dict(cow='moo') + expected_result = dict(mig_data) + if has_mig_data: + dest_check_data['migrate_data'] = dict(cat='meow') + expected_result.update(cat='meow') + + self.mox.StubOutWithMock(self.compute, '_get_compute_info') + self.mox.StubOutWithMock(self.compute.driver, + 'check_can_live_migrate_destination') + self.mox.StubOutWithMock(self.compute.compute_rpcapi, + 'check_can_live_migrate_source') + self.mox.StubOutWithMock(self.compute.driver, + 'check_can_live_migrate_destination_cleanup') + + self.compute._get_compute_info(self.context, + 'fake-host').AndReturn(src_info) + self.compute._get_compute_info(self.context, + CONF.host).AndReturn(dest_info) + self.compute.driver.check_can_live_migrate_destination( + self.context, instance, src_info, dest_info, + block_migration, disk_over_commit).AndReturn(dest_check_data) + + mock_meth = self.compute.compute_rpcapi.check_can_live_migrate_source( + self.context, instance, dest_check_data) + if do_raise: + mock_meth.AndRaise(test.TestingException()) + else: + mock_meth.AndReturn(mig_data) + self.compute.driver.check_can_live_migrate_destination_cleanup( + self.context, dest_check_data) + + self.mox.ReplayAll() + + result = self.compute.check_can_live_migrate_destination( + self.context, instance=instance, + block_migration=block_migration, + disk_over_commit=disk_over_commit) + self.assertEqual(expected_result, result) + + def test_check_can_live_migrate_destination_success(self): + self._test_check_can_live_migrate_destination() + + def test_check_can_live_migrate_destination_success_w_mig_data(self): + self._test_check_can_live_migrate_destination(has_mig_data=True) + + def test_check_can_live_migrate_destination_fail(self): + self.assertRaises( + test.TestingException, + self._test_check_can_live_migrate_destination, + do_raise=True) + class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase): def setUp(self): diff --git a/nova/tests/compute/test_rpcapi.py b/nova/tests/compute/test_rpcapi.py index 6287271b0bcd..b0ca93bbf34b 100644 --- a/nova/tests/compute/test_rpcapi.py +++ b/nova/tests/compute/test_rpcapi.py @@ -118,12 +118,14 @@ class ComputeRpcAPITestCase(test.TestCase): self._test_compute_api('check_can_live_migrate_destination', 'call', instance=self.fake_instance, destination='dest', block_migration=True, - disk_over_commit=True) + disk_over_commit=True, + version='2.38') def test_check_can_live_migrate_source(self): self._test_compute_api('check_can_live_migrate_source', 'call', instance=self.fake_instance, - dest_check_data={"test": "data"}) + dest_check_data={"test": "data"}, + version='2.38') def test_check_instance_shared_storage(self): self._test_compute_api('check_instance_shared_storage', 'call', diff --git a/nova/tests/conductor/tasks/test_live_migrate.py b/nova/tests/conductor/tasks/test_live_migrate.py index 89f3311bc5da..40f51ead6834 100644 --- a/nova/tests/conductor/tasks/test_live_migrate.py +++ b/nova/tests/conductor/tasks/test_live_migrate.py @@ -19,7 +19,10 @@ from nova.compute import power_state from nova.conductor.tasks import live_migrate from nova import db from nova import exception +from nova.objects import base as obj_base +from nova.objects import instance as instance_obj from nova import test +from nova.tests import fake_instance class LiveMigrationTaskTestCase(test.TestCase): @@ -29,12 +32,14 @@ class LiveMigrationTaskTestCase(test.TestCase): self.instance_host = "host" self.instance_uuid = "uuid" self.instance_image = "image_ref" - self.instance = { - "host": self.instance_host, - "uuid": self.instance_uuid, - "power_state": power_state.RUNNING, - "memory_mb": 512, - "image_ref": self.instance_image} + db_instance = fake_instance.fake_db_instance( + host=self.instance_host, + uuid=self.instance_uuid, + power_state=power_state.RUNNING, + memory_mb=512, + image_ref=self.instance_image) + self.instance = instance_obj.Instance._from_db_object( + self.context, instance_obj.Instance(), db_instance) self.destination = "destination" self.block_migration = "bm" self.disk_over_commit = "doc" @@ -255,7 +260,8 @@ class LiveMigrationTaskTestCase(test.TestCase): flavors.extract_flavor(self.instance).AndReturn("inst_type") # request_spec with no image set - request_spec = {'instance_properties': self.instance, + instance_p = obj_base.obj_to_primitive(self.instance) + request_spec = {'instance_properties': instance_p, 'instance_type': "inst_type", 'instance_uuids': [self.instance['uuid']]} self.task.scheduler_rpcapi.select_hosts(self.context, request_spec, diff --git a/nova/tests/conductor/test_conductor.py b/nova/tests/conductor/test_conductor.py index 69934de63aeb..a6a07e0d71d8 100644 --- a/nova/tests/conductor/test_conductor.py +++ b/nova/tests/conductor/test_conductor.py @@ -31,6 +31,7 @@ from nova import db from nova.db.sqlalchemy import models from nova import exception as exc from nova import notifications +from nova.objects import base as obj_base from nova.objects import instance as instance_obj from nova.openstack.common import jsonutils from nova.openstack.common.notifier import api as notifier_api @@ -1227,14 +1228,28 @@ class _BaseTaskTestCase(object): self.stubs.Set(rpc_common, 'catch_client_exception', passthru) def test_live_migrate(self): + inst = fake_instance.fake_db_instance() + inst_obj = instance_obj.Instance._from_db_object( + self.context, instance_obj.Instance(), inst, []) + self.mox.StubOutWithMock(live_migrate, 'execute') - live_migrate.execute(self.context, 'instance', 'destination', - 'block_migration', 'disk_over_commit') + live_migrate.execute(self.context, + mox.IsA(instance_obj.Instance), + 'destination', + 'block_migration', + 'disk_over_commit') self.mox.ReplayAll() - self.conductor.migrate_server(self.context, 'instance', - {'host': 'destination'}, True, False, None, - 'block_migration', 'disk_over_commit') + if isinstance(self.conductor, (conductor_api.ComputeTaskAPI, + conductor_api.LocalComputeTaskAPI)): + # The API method is actually 'live_migrate_instance'. It gets + # converted into 'migrate_server' when doing RPC. + self.conductor.live_migrate_instance(self.context, inst_obj, + 'destination', 'block_migration', 'disk_over_commit') + else: + self.conductor.migrate_server(self.context, inst_obj, + {'host': 'destination'}, True, False, None, + 'block_migration', 'disk_over_commit') def test_cold_migrate(self): self.mox.StubOutWithMock(self.conductor_manager.image_service, 'show') @@ -1243,94 +1258,44 @@ class _BaseTaskTestCase(object): self.conductor_manager.compute_rpcapi, 'prep_resize') self.mox.StubOutWithMock(self.conductor_manager.scheduler_rpcapi, 'select_destinations') - inst = { - 'uuid': 'fakeuuid', - 'image_ref': 'image_ref' - } + inst = fake_instance.fake_db_instance(image_ref='image_ref') + inst_obj = instance_obj.Instance._from_db_object( + self.context, instance_obj.Instance(), inst, []) instance_type = {} instance_type['extra_specs'] = 'extra_specs' request_spec = {'instance_type': instance_type} - self.conductor_manager.image_service.show( - self.context, 'image_ref').AndReturn({}) + self.context, inst_obj['image_ref']).AndReturn('image') scheduler_utils.build_request_spec( - self.context, {}, [inst]).AndReturn(request_spec) + self.context, 'image', + [mox.IsA(instance_obj.Instance)]).AndReturn(request_spec) hosts = [dict(host='host1', nodename=None, limits={})] self.conductor_manager.scheduler_rpcapi.select_destinations( self.context, request_spec, {}).AndReturn(hosts) filter_properties = {'limits': {}} + self.conductor_manager.compute_rpcapi.prep_resize( - self.context, {}, inst, 'flavor', 'host1', - [], request_spec=request_spec, + self.context, 'image', mox.IsA(dict), 'flavor', + 'host1', [], request_spec=request_spec, filter_properties=filter_properties, node=None) self.mox.ReplayAll() scheduler_hint = {'filter_properties': {}} - self.conductor.migrate_server( - self.context, inst, scheduler_hint, - False, False, 'flavor', None, None, []) - def test_migrate_server_fails_with_rebuild(self): - self.assertRaises(NotImplementedError, self.conductor.migrate_server, - self.context, None, None, True, True, None, None, None) - - def _build_request_spec(self, instance): - return { - 'instance_properties': { - 'uuid': instance['uuid'], }, - } - - def test_migrate_server_deals_with_expected_exceptions(self): - instance = {"uuid": "uuid", - "vm_state": vm_states.ACTIVE} - self.mox.StubOutWithMock(live_migrate, 'execute') - self.mox.StubOutWithMock(scheduler_utils, - 'set_vm_state_and_notify') - - ex = exc.DestinationHypervisorTooOld() - live_migrate.execute(self.context, instance, 'destination', - 'block_migration', 'disk_over_commit').AndRaise(ex) - - scheduler_utils.set_vm_state_and_notify(self.context, - 'compute_task', 'migrate_server', - {'vm_state': vm_states.ACTIVE, - 'task_state': None, - 'expected_task_state': task_states.MIGRATING}, - ex, self._build_request_spec(instance), - self.conductor_manager.db) - self.mox.ReplayAll() - - self.stub_out_client_exceptions() - self.assertRaises(exc.DestinationHypervisorTooOld, - self.conductor.migrate_server, self.context, instance, - {'host': 'destination'}, True, False, None, 'block_migration', - 'disk_over_commit') - - def test_migrate_server_deals_with_unexpected_exceptions(self): - instance = {"uuid": "uuid"} - self.mox.StubOutWithMock(live_migrate, 'execute') - self.mox.StubOutWithMock(scheduler_utils, - 'set_vm_state_and_notify') - - ex = IOError() - live_migrate.execute(self.context, instance, 'destination', - 'block_migration', 'disk_over_commit').AndRaise(ex) - scheduler_utils.set_vm_state_and_notify(self.context, - 'compute_task', 'migrate_server', - {'vm_state': vm_states.ERROR}, - ex, self._build_request_spec(instance), - self.conductor_manager.db) - self.mox.ReplayAll() - - self.stub_out_client_exceptions() - self.assertRaises(IOError, - self.conductor.migrate_server, self.context, instance, - {'host': 'destination'}, True, False, None, 'block_migration', - 'disk_over_commit') + if isinstance(self.conductor, (conductor_api.ComputeTaskAPI, + conductor_api.LocalComputeTaskAPI)): + # The API method is actually 'resize_instance'. It gets + # converted into 'migrate_server' when doing RPC. + self.conductor.resize_instance( + self.context, inst_obj, {}, scheduler_hint, 'flavor', []) + else: + self.conductor.migrate_server( + self.context, inst_obj, scheduler_hint, + False, False, 'flavor', None, None, []) def test_build_instances(self): instance_type = flavors.get_default_flavor() @@ -1475,6 +1440,66 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase): self.assertRaises(NotImplementedError, self.conductor.migrate_server, self.context, None, None, True, False, "dummy", None, None) + def _build_request_spec(self, instance): + return { + 'instance_properties': { + 'uuid': instance['uuid'], }, + } + + def test_migrate_server_deals_with_expected_exceptions(self): + instance = fake_instance.fake_db_instance(uuid='uuid', + vm_state=vm_states.ACTIVE) + inst_obj = instance_obj.Instance._from_db_object( + self.context, instance_obj.Instance(), instance, []) + self.mox.StubOutWithMock(live_migrate, 'execute') + self.mox.StubOutWithMock(scheduler_utils, + 'set_vm_state_and_notify') + + ex = exc.DestinationHypervisorTooOld() + live_migrate.execute(self.context, mox.IsA(instance_obj.Instance), + 'destination', 'block_migration', + 'disk_over_commit').AndRaise(ex) + + scheduler_utils.set_vm_state_and_notify(self.context, + 'compute_task', 'migrate_server', + {'vm_state': vm_states.ACTIVE, + 'task_state': None, + 'expected_task_state': task_states.MIGRATING}, + ex, self._build_request_spec(inst_obj), + self.conductor_manager.db) + self.mox.ReplayAll() + + self.stub_out_client_exceptions() + self.assertRaises(exc.DestinationHypervisorTooOld, + self.conductor.migrate_server, self.context, inst_obj, + {'host': 'destination'}, True, False, None, 'block_migration', + 'disk_over_commit') + + def test_migrate_server_deals_with_unexpected_exceptions(self): + instance = fake_instance.fake_db_instance() + inst_obj = instance_obj.Instance._from_db_object( + self.context, instance_obj.Instance(), instance, []) + self.mox.StubOutWithMock(live_migrate, 'execute') + self.mox.StubOutWithMock(scheduler_utils, + 'set_vm_state_and_notify') + + ex = IOError() + live_migrate.execute(self.context, mox.IsA(instance_obj.Instance), + 'destination', 'block_migration', + 'disk_over_commit').AndRaise(ex) + scheduler_utils.set_vm_state_and_notify(self.context, + 'compute_task', 'migrate_server', + {'vm_state': vm_states.ERROR}, + ex, self._build_request_spec(inst_obj), + self.conductor_manager.db) + self.mox.ReplayAll() + + self.stub_out_client_exceptions() + self.assertRaises(IOError, + self.conductor.migrate_server, self.context, inst_obj, + {'host': 'destination'}, True, False, None, 'block_migration', + 'disk_over_commit') + def test_set_vm_state_and_notify(self): self.mox.StubOutWithMock(scheduler_utils, 'set_vm_state_and_notify') @@ -1485,10 +1510,13 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase): self.mox.ReplayAll() self.conductor._set_vm_state_and_notify( - self.context, 'method', 'updates', 'ex', 'request_spec') + self.context, 'method', 'updates', 'ex', 'request_spec') def test_cold_migrate_no_valid_host_back_in_active_state(self): inst = fake_instance.fake_db_instance(image_ref='fake-image_ref') + inst_obj = instance_obj.Instance._from_db_object( + self.context, instance_obj.Instance(), inst, + expected_attrs=[]) request_spec = dict(instance_type=dict(extra_specs=dict())) filter_props = dict(context=None) resvs = 'fake-resvs' @@ -1505,7 +1533,7 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase): self.conductor._get_image(self.context, 'fake-image_ref').AndReturn(image) scheduler_utils.build_request_spec( - self.context, image, [inst]).AndReturn(request_spec) + self.context, image, [inst_obj]).AndReturn(request_spec) exc_info = exc.NoValidHost(reason="") @@ -1524,12 +1552,15 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase): self.mox.ReplayAll() - self.conductor._cold_migrate(self.context, inst, + self.conductor._cold_migrate(self.context, inst_obj, 'flavor', filter_props, resvs) def test_cold_migrate_no_valid_host_back_in_stopped_state(self): - inst = fake_instance.fake_db_instance(image_ref='fake-image_ref') - inst['vm_state'] = vm_states.STOPPED + inst = fake_instance.fake_db_instance(image_ref='fake-image_ref', + vm_state=vm_states.STOPPED) + inst_obj = instance_obj.Instance._from_db_object( + self.context, instance_obj.Instance(), inst, + expected_attrs=[]) request_spec = dict(instance_type=dict(extra_specs=dict())) filter_props = dict(context=None) resvs = 'fake-resvs' @@ -1546,7 +1577,7 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase): self.conductor._get_image(self.context, 'fake-image_ref').AndReturn(image) scheduler_utils.build_request_spec( - self.context, image, [inst]).AndReturn(request_spec) + self.context, image, [inst_obj]).AndReturn(request_spec) exc_info = exc.NoValidHost(reason="") @@ -1565,11 +1596,14 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase): self.mox.ReplayAll() - self.conductor._cold_migrate(self.context, inst, + self.conductor._cold_migrate(self.context, inst_obj, 'flavor', filter_props, resvs) def test_cold_migrate_exception_host_in_error_state_and_raise(self): inst = fake_instance.fake_db_instance(image_ref='fake-image_ref') + inst_obj = instance_obj.Instance._from_db_object( + self.context, instance_obj.Instance(), inst, + expected_attrs=[]) request_spec = dict(instance_type=dict(extra_specs=dict())) filter_props = dict(context=None) resvs = 'fake-resvs' @@ -1591,7 +1625,7 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase): self.conductor._get_image(self.context, 'fake-image_ref').AndReturn(image) scheduler_utils.build_request_spec( - self.context, image, [inst]).AndReturn(request_spec) + self.context, image, [inst_obj]).AndReturn(request_spec) exc_info = exc.NoValidHost(reason="") @@ -1609,7 +1643,7 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase): exc_info = test.TestingException('something happened') self.conductor.compute_rpcapi.prep_resize( - self.context, image, inst, + self.context, image, obj_base.obj_to_primitive(inst_obj), 'flavor', hosts[0]['host'], resvs, request_spec=expected_request_spec, filter_properties=expected_filter_props, @@ -1628,7 +1662,7 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase): self.assertRaises(test.TestingException, self.conductor._cold_migrate, - self.context, inst, 'flavor', + self.context, inst_obj, 'flavor', filter_props, resvs) diff --git a/nova/tests/objects/test_migration.py b/nova/tests/objects/test_migration.py index ab1dffc3715c..c48c6453897b 100644 --- a/nova/tests/objects/test_migration.py +++ b/nova/tests/objects/test_migration.py @@ -23,22 +23,29 @@ from nova.tests.objects import test_objects NOW = timeutils.utcnow().replace(microsecond=0) -fake_migration = { - 'created_at': NOW, - 'updated_at': None, - 'deleted_at': None, - 'deleted': False, - 'id': 123, - 'source_compute': 'compute-source', - 'dest_compute': 'compute-dest', - 'source_node': 'node-source', - 'dest_node': 'node-dest', - 'dest_host': 'host-dest', - 'old_instance_type_id': 42, - 'new_instance_type_id': 84, - 'instance_uuid': 'fake-uuid', - 'status': 'migrating', -} + + +def fake_db_migration(**updates): + db_instance = { + 'created_at': NOW, + 'updated_at': None, + 'deleted_at': None, + 'deleted': False, + 'id': 123, + 'source_compute': 'compute-source', + 'dest_compute': 'compute-dest', + 'source_node': 'node-source', + 'dest_node': 'node-dest', + 'dest_host': 'host-dest', + 'old_instance_type_id': 42, + 'new_instance_type_id': 84, + 'instance_uuid': 'fake-uuid', + 'status': 'migrating', + } + + if updates: + db_instance.update(updates) + return db_instance class _TestMigrationObject(object): @@ -52,6 +59,7 @@ class _TestMigrationObject(object): def test_get_by_id(self): ctxt = context.get_admin_context() + fake_migration = fake_db_migration() self.mox.StubOutWithMock(db, 'migration_get') db.migration_get(ctxt, fake_migration['id']).AndReturn(fake_migration) self.mox.ReplayAll() @@ -60,6 +68,7 @@ class _TestMigrationObject(object): def test_get_by_instance_and_status(self): ctxt = context.get_admin_context() + fake_migration = fake_db_migration() self.mox.StubOutWithMock(db, 'migration_get_by_instance_and_status') db.migration_get_by_instance_and_status(ctxt, fake_migration['id'], @@ -72,6 +81,7 @@ class _TestMigrationObject(object): def test_create(self): 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) @@ -83,6 +93,7 @@ class _TestMigrationObject(object): def test_save(self): ctxt = context.get_admin_context() + fake_migration = fake_db_migration() self.mox.StubOutWithMock(db, 'migration_update') db.migration_update(ctxt, 123, {'source_compute': 'foo'} ).AndReturn(fake_migration) @@ -95,6 +106,7 @@ class _TestMigrationObject(object): def test_instance(self): ctxt = context.get_admin_context() + fake_migration = fake_db_migration() fake_inst = fake_instance.fake_db_instance() self.mox.StubOutWithMock(db, 'instance_get_by_uuid') db.instance_get_by_uuid(ctxt, fake_migration['instance_uuid'], @@ -109,6 +121,7 @@ class _TestMigrationObject(object): def test_get_unconfirmed_by_dest_compute(self): ctxt = context.get_admin_context() + fake_migration = fake_db_migration() db_migrations = [fake_migration, dict(fake_migration, id=456)] self.mox.StubOutWithMock( db, 'migration_get_unconfirmed_by_dest_compute') @@ -124,6 +137,7 @@ class _TestMigrationObject(object): def test_get_in_progress_by_host_and_node(self): ctxt = context.get_admin_context() + fake_migration = fake_db_migration() db_migrations = [fake_migration, dict(fake_migration, id=456)] self.mox.StubOutWithMock( db, 'migration_get_in_progress_by_host_and_node') @@ -139,6 +153,7 @@ class _TestMigrationObject(object): def test_get_by_filters(self): ctxt = context.get_admin_context() + fake_migration = fake_db_migration() db_migrations = [fake_migration, dict(fake_migration, id=456)] self.mox.StubOutWithMock( db, 'migration_get_all_by_filters')