diff --git a/nova/compute/manager.py b/nova/compute/manager.py index baf44f93c0fa..0d169e7a4cc4 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -1159,6 +1159,8 @@ class ComputeManager(manager.Manager): try_reboot, reboot_type = self._retry_reboot( instance, current_power_state) + # NOTE(amorin) + # If the instance is in power_state_SHUTDOWN, we will try_reboot if try_reboot: LOG.debug("Instance in transitional state (%(task_state)s) at " "start-up and power state is (%(power_state)s), " @@ -1181,9 +1183,14 @@ class ComputeManager(manager.Manager): reboot_type=reboot_type) return + # NOTE(plestang): an instance might be in power_state.RUNNING with a + # transient state when a host is brutally shutdown or rebooted while a + # reboot/pause/unpause is scheduled on client side elif (current_power_state == power_state.RUNNING and instance.task_state in [task_states.REBOOT_STARTED, task_states.REBOOT_STARTED_HARD, + task_states.REBOOTING_HARD, + task_states.REBOOTING, task_states.PAUSING, task_states.UNPAUSING]): LOG.warning("Instance in transitional state " @@ -1343,7 +1350,9 @@ class ComputeManager(manager.Manager): current_task_state == task_states.REBOOT_PENDING_HARD and instance.vm_state in vm_states.ALLOW_HARD_REBOOT) started_not_running = (current_task_state in - [task_states.REBOOT_STARTED, + [task_states.REBOOTING, + task_states.REBOOTING_HARD, + task_states.REBOOT_STARTED, task_states.REBOOT_STARTED_HARD] and current_power_state != power_state.RUNNING) diff --git a/nova/tests/unit/compute/test_compute_mgr.py b/nova/tests/unit/compute/test_compute_mgr.py index 249d1a5302d5..0b16fb821880 100644 --- a/nova/tests/unit/compute/test_compute_mgr.py +++ b/nova/tests/unit/compute/test_compute_mgr.py @@ -2278,18 +2278,30 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase, instance.host = self.compute.host with test.nested( mock.patch.object(self.compute, '_get_power_state', - return_value=power_state.RUNNING), + return_value=instance.power_state), mock.patch.object(instance, 'save', autospec=True), - mock.patch.object(objects.Instance, 'get_network_info') + mock.patch.object(objects.Instance, 'get_network_info'), + mock.patch.object(self.compute, 'reboot_instance'), ) as ( _get_power_state, instance_save, - get_network_info + get_network_info, + reboot_instance, ): self.compute._init_instance(self.context, instance) - instance_save.assert_called_once_with() - self.assertIsNone(instance.task_state) - self.assertEqual(vm_states.ACTIVE, instance.vm_state) + # If instance was running, the instance should stay running, + # and tasks are discarded + if instance.power_state == power_state.RUNNING: + instance_save.assert_called_once_with() + self.assertIsNone(instance.task_state) + self.assertEqual(vm_states.ACTIVE, instance.vm_state) + # If instance was not running, but we request to reboot it, + # we should try a reboot + else: + call = mock.call(self.context, instance, + block_device_info=None, reboot_type=mock.ANY) + reboot_instance.assert_has_calls([call]) + self.assertEqual(vm_states.STOPPED, instance.vm_state) def test_init_instance_cleans_image_state_reboot_started(self): instance = objects.Instance(self.context) @@ -2307,37 +2319,13 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase, instance.power_state = power_state.RUNNING self._test_init_instance_cleans_reboot_state(instance) - def _test_init_instance_cleans_reboot_state_reproducer(self, instance, - task_state): - instance.host = self.compute.host - with test.nested( - mock.patch.object(self.compute, '_get_power_state', - return_value=power_state.RUNNING), - mock.patch.object(instance, 'save', autospec=True), - mock.patch.object(objects.Instance, 'get_network_info') - ) as ( - _get_power_state, - instance_save, - get_network_info - ): - self.compute._init_instance(self.context, instance) - # By checking save method is not called we confirm that the init - # instance does not take into account this use case - instance_save.assert_not_called() - # So the instance task_state is still task_state - self.assertEqual(task_state, instance.task_state) - self.assertEqual(vm_states.ACTIVE, instance.vm_state) - def test_init_instance_cleans_image_state_rebooting(self): instance = objects.Instance(self.context) instance.uuid = uuids.instance instance.vm_state = vm_states.ACTIVE instance.task_state = task_states.REBOOTING instance.power_state = power_state.RUNNING - # To uncomment once bug #1999674 is fixed and remove all the code below - # self._test_init_instance_cleans_reboot_state(instance) - self._test_init_instance_cleans_reboot_state_reproducer(instance, - task_states.REBOOTING) + self._test_init_instance_cleans_reboot_state(instance) def test_init_instance_cleans_image_state_rebooting_hard(self): instance = objects.Instance(self.context) @@ -2345,10 +2333,39 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase, instance.vm_state = vm_states.ACTIVE instance.task_state = task_states.REBOOTING_HARD instance.power_state = power_state.RUNNING - # To uncomment once bug #1999674 is fixed and remove all the code below - # self._test_init_instance_cleans_reboot_state(instance) - self._test_init_instance_cleans_reboot_state_reproducer(instance, - task_states.REBOOTING_HARD) + self._test_init_instance_cleans_reboot_state(instance) + + def test_init_instance_cleans_shutdown_reboot_started(self): + instance = objects.Instance(self.context) + instance.uuid = uuids.instance + instance.vm_state = vm_states.STOPPED + instance.task_state = task_states.REBOOT_STARTED + instance.power_state = power_state.SHUTDOWN + self._test_init_instance_cleans_reboot_state(instance) + + def test_init_instance_cleans_shutdown_reboot_started_hard(self): + instance = objects.Instance(self.context) + instance.uuid = uuids.instance + instance.vm_state = vm_states.STOPPED + instance.task_state = task_states.REBOOT_STARTED_HARD + instance.power_state = power_state.SHUTDOWN + self._test_init_instance_cleans_reboot_state(instance) + + def test_init_instance_cleans_shutdown_rebooting(self): + instance = objects.Instance(self.context) + instance.uuid = uuids.instance + instance.vm_state = vm_states.STOPPED + instance.task_state = task_states.REBOOTING + instance.power_state = power_state.SHUTDOWN + self._test_init_instance_cleans_reboot_state(instance) + + def test_init_instance_cleans_shutdown_rebooting_hard(self): + instance = objects.Instance(self.context) + instance.uuid = uuids.instance + instance.vm_state = vm_states.STOPPED + instance.task_state = task_states.REBOOTING_HARD + instance.power_state = power_state.SHUTDOWN + self._test_init_instance_cleans_reboot_state(instance) def test_init_instance_retries_power_off(self): instance = objects.Instance(self.context)