From 13ad851e49a4bcadf65ebaf1c437fcbcae8659de Mon Sep 17 00:00:00 2001 From: Matt Riedemann Date: Wed, 28 May 2014 13:09:44 -0700 Subject: [PATCH] Refactor test_rescue_unrescue into compute api/manager unit tests As part of converting the rescue/unrescue compute API flows to using objects, i.e. dot notation for accessing attributes on the instance object and updating the database via instance.save() rather than compute_api.update, we want to move some of the integration-style tests found in test_compute.py to test_compute_api.py and test_compute_mgr.py. This is to help with the conversion so we don't have to use exotic stubs in the cells API flows to make integration tests work. This change is mainly concerned with moving test_rescue_unrescue out of test_compute.py and into the api/manager specific test modules and then later changes will build on top of the new tests. Part of blueprint compute-manager-objects-juno Change-Id: I2a9b1c6b5a1f41272e26c20de134a2a8b71c88f3 --- nova/tests/compute/test_compute.py | 29 ------- nova/tests/compute/test_compute_api.py | 62 ++++++++++++++ nova/tests/compute/test_compute_mgr.py | 110 +++++++++++++++++++++++++ 3 files changed, 172 insertions(+), 29 deletions(-) diff --git a/nova/tests/compute/test_compute.py b/nova/tests/compute/test_compute.py index 943cf85f0a30..f6f261af9c5b 100644 --- a/nova/tests/compute/test_compute.py +++ b/nova/tests/compute/test_compute.py @@ -7443,35 +7443,6 @@ class ComputeAPITestCase(BaseTestCase): self.compute.terminate_instance(self.context, self._objectify(inst_ref), [], []) - def test_rescue_unrescue(self): - instance = jsonutils.to_primitive(self._create_fake_instance()) - instance_uuid = instance['uuid'] - self.compute.run_instance(self.context, instance, {}, {}, None, None, - None, True, None, False) - - instance = db.instance_get_by_uuid(self.context, instance_uuid) - self.assertEqual(instance['vm_state'], vm_states.ACTIVE) - self.assertIsNone(instance['task_state']) - - self.compute_api.rescue(self.context, self._objectify(instance)) - - instance = db.instance_get_by_uuid(self.context, instance_uuid) - self.assertEqual(instance['vm_state'], vm_states.ACTIVE) - self.assertEqual(instance['task_state'], task_states.RESCUING) - - params = {'vm_state': vm_states.RESCUED, 'task_state': None} - db.instance_update(self.context, instance_uuid, params) - - instance = db.instance_get_by_uuid(self.context, instance_uuid) - self.compute_api.unrescue(self.context, self._objectify(instance)) - - instance = db.instance_get_by_uuid(self.context, instance_uuid) - self.assertEqual(instance['vm_state'], vm_states.RESCUED) - self.assertEqual(instance['task_state'], task_states.UNRESCUING) - - self.compute.terminate_instance(self.context, - self._objectify(instance), [], []) - def _fake_rescue_block_devices(self, instance, status="in-use"): fake_bdms = block_device_obj.block_device_make_list(self.context, [fake_block_device.FakeDbBlockDeviceDict( diff --git a/nova/tests/compute/test_compute_api.py b/nova/tests/compute/test_compute_api.py index 9c977078841b..168aeb48c46a 100644 --- a/nova/tests/compute/test_compute_api.py +++ b/nova/tests/compute/test_compute_api.py @@ -13,6 +13,7 @@ """Unit tests for compute API.""" +import contextlib import copy import datetime import iso8601 @@ -2004,6 +2005,67 @@ class _ComputeAPIUnitTestMixIn(object): fake_index) destroy.assert_called_once_with(self.context) + def _test_rescue(self, vm_state): + instance = self._create_instance_obj(params={'vm_state': vm_state}) + bdms = [] + with contextlib.nested( + mock.patch.object(block_device_obj.BlockDeviceMappingList, + 'get_by_instance_uuid', return_value=bdms), + mock.patch.object(self.compute_api, 'is_volume_backed_instance', + return_value=False), + mock.patch.object(self.compute_api, 'update'), + mock.patch.object(self.compute_api, '_record_action_start'), + mock.patch.object(self.compute_api.compute_rpcapi, + 'rescue_instance') + ) as ( + bdm_get_by_instance_uuid, volume_backed_inst, compute_api_update, + record_action_start, rpcapi_rescue_instance + ): + self.compute_api.rescue(self.context, instance) + # assert our mock calls + bdm_get_by_instance_uuid.assert_called_once_with( + self.context, instance.uuid) + volume_backed_inst.assert_called_once_with( + self.context, instance, bdms) + compute_api_update.assert_called_once_with( + self.context, instance, task_state=task_states.RESCUING, + expected_task_state=[None]) + record_action_start.assert_called_once_with( + self.context, instance, instance_actions.RESCUE) + rpcapi_rescue_instance.assert_called_once_with( + self.context, instance=instance, rescue_password=None, + rescue_image_ref=None) + + def test_rescue_active(self): + self._test_rescue(vm_state=vm_states.ACTIVE) + + def test_rescue_stopped(self): + self._test_rescue(vm_state=vm_states.STOPPED) + + def test_rescue_error(self): + self._test_rescue(vm_state=vm_states.ERROR) + + def test_unrescue(self): + instance = self._create_instance_obj( + params={'vm_state': vm_states.RESCUED}) + with contextlib.nested( + mock.patch.object(self.compute_api, 'update'), + mock.patch.object(self.compute_api, '_record_action_start'), + mock.patch.object(self.compute_api.compute_rpcapi, + 'unrescue_instance') + ) as ( + compute_api_update, record_action_start, rpcapi_unrescue_instance + ): + self.compute_api.unrescue(self.context, instance) + # assert our mock calls + compute_api_update.assert_called_once_with( + self.context, instance, task_state=task_states.UNRESCUING, + expected_task_state=[None]) + record_action_start.assert_called_once_with( + self.context, instance, instance_actions.UNRESCUE) + rpcapi_unrescue_instance.assert_called_once_with( + self.context, instance=instance) + class ComputeAPIUnitTestCase(_ComputeAPIUnitTestMixIn, test.NoDBTestCase): def setUp(self): diff --git a/nova/tests/compute/test_compute_mgr.py b/nova/tests/compute/test_compute_mgr.py index 43169a82fa98..8a678f96ad33 100644 --- a/nova/tests/compute/test_compute_mgr.py +++ b/nova/tests/compute/test_compute_mgr.py @@ -1211,6 +1211,116 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase): {'uuid': 'fake-inst'}) detach.assert_called_once_with(self.context, inst_obj, bdm) + def test_rescue(self): + instance = fake_instance.fake_instance_obj( + self.context, vm_state=vm_states.ACTIVE) + fake_nw_info = network_model.NetworkInfo() + rescue_image_meta = {'id': 'fake', 'name': 'fake'} + with contextlib.nested( + mock.patch.object(instance_action_obj.InstanceActionEvent, + 'event_start'), + mock.patch.object(instance_action_obj.InstanceActionEvent, + 'event_finish_with_failure'), + mock.patch.object(self.context, 'elevated', + return_value=self.context), + mock.patch.object(self.compute, '_get_instance_nw_info', + return_value=fake_nw_info), + mock.patch.object(self.compute, '_get_rescue_image', + return_value=rescue_image_meta), + mock.patch.object(self.compute, '_notify_about_instance_usage'), + mock.patch.object(self.compute.driver, 'rescue'), + mock.patch.object(self.compute.conductor_api, + 'notify_usage_exists'), + mock.patch.object(self.compute, '_get_power_state', + return_value=power_state.RUNNING), + mock.patch.object(instance, 'save') + ) as ( + event_start, event_finish, elevated_context, get_nw_info, + get_rescue_image, notify_instance_usage, driver_rescue, + notify_usage_exists, get_power_state, instance_save + ): + self.compute.rescue_instance( + self.context, instance, rescue_password='verybadpass', + rescue_image_ref=None) + + # assert the field values on the instance object + self.assertEqual(vm_states.RESCUED, instance.vm_state) + self.assertIsNone(instance.task_state) + self.assertEqual(power_state.RUNNING, instance.power_state) + self.assertIsNotNone(instance.launched_at) + + # assert our mock calls + get_nw_info.assert_called_once_with(self.context, instance) + get_rescue_image.assert_called_once_with( + self.context, instance, None) + + extra_usage_info = {'rescue_image_name': 'fake'} + notify_calls = [ + mock.call(self.context, instance, "rescue.start", + extra_usage_info=extra_usage_info, + network_info=fake_nw_info), + mock.call(self.context, instance, "rescue.end", + extra_usage_info=extra_usage_info, + network_info=fake_nw_info) + ] + notify_instance_usage.assert_has_calls(notify_calls) + + driver_rescue.assert_called_once_with( + self.context, instance, fake_nw_info, rescue_image_meta, + 'verybadpass') + + notify_usage_exists.assert_called_once_with( + self.context, instance, current_period=True) + + instance_save.assert_called_once_with( + expected_task_state=task_states.RESCUING) + + def test_unrescue(self): + instance = fake_instance.fake_instance_obj( + self.context, vm_state=vm_states.RESCUED) + fake_nw_info = network_model.NetworkInfo() + with contextlib.nested( + mock.patch.object(instance_action_obj.InstanceActionEvent, + 'event_start'), + mock.patch.object(instance_action_obj.InstanceActionEvent, + 'event_finish_with_failure'), + mock.patch.object(self.context, 'elevated', + return_value=self.context), + mock.patch.object(self.compute, '_get_instance_nw_info', + return_value=fake_nw_info), + mock.patch.object(self.compute, '_notify_about_instance_usage'), + mock.patch.object(self.compute.driver, 'unrescue'), + mock.patch.object(self.compute, '_get_power_state', + return_value=power_state.RUNNING), + mock.patch.object(instance, 'save') + ) as ( + event_start, event_finish, elevated_context, get_nw_info, + notify_instance_usage, driver_unrescue, get_power_state, + instance_save + ): + self.compute.unrescue_instance(self.context, instance) + + # assert the field values on the instance object + self.assertEqual(vm_states.ACTIVE, instance.vm_state) + self.assertIsNone(instance.task_state) + self.assertEqual(power_state.RUNNING, instance.power_state) + + # assert our mock calls + get_nw_info.assert_called_once_with(self.context, instance) + + notify_calls = [ + mock.call(self.context, instance, "unrescue.start", + network_info=fake_nw_info), + mock.call(self.context, instance, "unrescue.end", + network_info=fake_nw_info) + ] + notify_instance_usage.assert_has_calls(notify_calls) + + driver_unrescue.assert_called_once_with(instance, fake_nw_info) + + instance_save.assert_called_once_with( + expected_task_state=task_states.UNRESCUING) + class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase): def setUp(self):