diff --git a/nova/conductor/api.py b/nova/conductor/api.py index 16b28d45fe..8171af8446 100644 --- a/nova/conductor/api.py +++ b/nova/conductor/api.py @@ -180,3 +180,8 @@ class ComputeTaskAPI(object): self, ctxt, instance, migration, do_cast=True): self.conductor_compute_rpcapi.confirm_snapshot_based_resize( ctxt, instance, migration, do_cast=do_cast) + + def revert_snapshot_based_resize( + self, ctxt, instance, migration): + self.conductor_compute_rpcapi.revert_snapshot_based_resize( + ctxt, instance, migration) diff --git a/nova/conductor/manager.py b/nova/conductor/manager.py index 987314642c..055c07f875 100644 --- a/nova/conductor/manager.py +++ b/nova/conductor/manager.py @@ -233,7 +233,7 @@ class ComputeTaskManager(base.Base): may involve coordinating activities on multiple compute nodes. """ - target = messaging.Target(namespace='compute_task', version='1.22') + target = messaging.Target(namespace='compute_task', version='1.23') def __init__(self): super(ComputeTaskManager, self).__init__() @@ -1875,3 +1875,22 @@ class ComputeTaskManager(base.Base): task = cross_cell_migrate.ConfirmResizeTask( context, instance, migration, self.notifier, self.compute_rpcapi) task.execute() + + @targets_cell + # FIXME(mriedem): Upon successful completion of RevertResizeTask the + # instance is hard-deleted, along with its instance action record(s), from + # the target cell database so wrap_instance_event hits + # InstanceActionNotFound on __exit__. + @wrap_instance_event(prefix='conductor') + def revert_snapshot_based_resize(self, context, instance, migration): + """Executes the RevertResizeTask + + :param context: nova auth request context targeted at the target cell + :param instance: Instance object in "resized" status from the target + cell + :param migration: Migration object from the target cell for the resize + operation expected to have status "reverting" + """ + task = cross_cell_migrate.RevertResizeTask( + context, instance, migration, self.notifier, self.compute_rpcapi) + task.execute() diff --git a/nova/conductor/rpcapi.py b/nova/conductor/rpcapi.py index d2a45ac128..28311af31b 100644 --- a/nova/conductor/rpcapi.py +++ b/nova/conductor/rpcapi.py @@ -285,6 +285,7 @@ class ComputeTaskAPI(object): potential alternate hosts for retries within a cell. 1.21 - Added cache_images() 1.22 - Added confirm_snapshot_based_resize() + 1.23 - Added revert_snapshot_based_resize() """ def __init__(self): @@ -465,3 +466,11 @@ class ComputeTaskAPI(object): if do_cast: return cctxt.cast(ctxt, 'confirm_snapshot_based_resize', **kw) return cctxt.call(ctxt, 'confirm_snapshot_based_resize', **kw) + + def revert_snapshot_based_resize(self, ctxt, instance, migration): + version = '1.23' + if not self.client.can_send_version(version): + raise exception.ServiceTooOld(_('nova-conductor too old')) + kw = {'instance': instance, 'migration': migration} + cctxt = self.client.prepare(version=version) + return cctxt.cast(ctxt, 'revert_snapshot_based_resize', **kw) diff --git a/nova/tests/unit/conductor/test_conductor.py b/nova/tests/unit/conductor/test_conductor.py index efae63e612..cdc8f1ce41 100644 --- a/nova/tests/unit/conductor/test_conductor.py +++ b/nova/tests/unit/conductor/test_conductor.py @@ -1783,6 +1783,18 @@ class _BaseTaskTestCase(object): self.context, instance=instance, migration=migration) mock_execute.assert_called_once_with() + @mock.patch( + 'nova.conductor.tasks.cross_cell_migrate.RevertResizeTask.execute') + def test_revert_snapshot_based_resize(self, mock_execute): + instance = self._create_fake_instance_obj(ctxt=self.context) + migration = objects.Migration( + context=self.context, source_compute=instance.host, + source_node=instance.node, instance_uuid=instance.uuid, + status='reverting', migration_type='migration') + self.conductor_manager.revert_snapshot_based_resize( + self.context, instance=instance, migration=migration) + mock_execute.assert_called_once_with() + class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase): """ComputeTaskManager Tests.""" @@ -3843,6 +3855,17 @@ class ConductorTaskRPCAPITestCase(_BaseTaskTestCase, self.context, mock.sentinel.instance, mock.sentinel.migration) + def test_revert_snapshot_based_resize_old_service(self): + """Tests revert_snapshot_based_resize when the service is too old.""" + with mock.patch.object( + self.conductor.client, 'can_send_version', + return_value=False) as can_send_version: + self.assertRaises(exc.ServiceTooOld, + self.conductor.revert_snapshot_based_resize, + self.context, mock.sentinel.instance, + mock.sentinel.migration) + can_send_version.assert_called_once_with('1.23') + class ConductorTaskAPITestCase(_BaseTaskTestCase, test_compute.BaseTestCase): """Compute task API Tests."""