diff options
-rwxr-xr-x | nova/compute/manager.py | 48 | ||||
-rw-r--r-- | nova/tests/compute/test_compute.py | 65 |
2 files changed, 84 insertions, 29 deletions
diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 393e96073..11ec70e44 100755 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -27,6 +27,7 @@ terminating it. """ + import base64 import contextlib import functools @@ -1817,14 +1818,8 @@ class ComputeManager(manager.SchedulerDependentManager): current_power_state = self._get_power_state(context, instance) - # Don't change it out of rescue mode - new_vm_state = vm_states.ACTIVE - if instance['vm_state'] == vm_states.RESCUED: - new_vm_state = vm_states.RESCUED - instance = self._instance_update(context, instance['uuid'], - power_state=current_power_state, - vm_state=new_vm_state) + power_state=current_power_state) if instance['power_state'] != power_state.RUNNING: state = instance['power_state'] @@ -1839,23 +1834,46 @@ class ComputeManager(manager.SchedulerDependentManager): context, instance, bad_devices, block_device_info) try: + # Don't change it out of rescue mode + if instance['vm_state'] == vm_states.RESCUED: + new_vm_state = vm_states.RESCUED + else: + new_vm_state = vm_states.ACTIVE + new_power_state = None + self.driver.reboot(context, instance, self._legacy_nw_info(network_info), reboot_type, block_device_info=block_device_info, bad_volumes_callback=bad_volumes_callback) - except Exception as exc: - LOG.error(_('Cannot reboot instance: %s'), exc, - context=context, instance=instance) + + except Exception as error: + # Can't use save_and_reraise as we don't know yet if we + # will re-raise or not + type_, value, tb = sys.exc_info() + compute_utils.add_instance_fault_from_exc(context, - self.conductor_api, instance, exc, sys.exc_info()) - # Fall through and reset task_state to None + self.conductor_api, instance, error, + sys.exc_info()) + + # if the reboot failed but the VM is running don't + # put it into an error state + new_power_state = self._get_power_state(context, instance) + if new_power_state == power_state.RUNNING: + LOG.warning(_('Reboot failed but instance is running'), + context=context, instance=instance) + else: + LOG.error(_('Cannot reboot instance: %(error)s'), + locals(), context=context, instance=instance) + self._set_instance_error_state(context, instance['uuid']) + raise type_, value, tb - current_power_state = self._get_power_state(context, instance) + if not new_power_state: + new_power_state = self._get_power_state(context, instance) try: instance = self._instance_update(context, instance['uuid'], - power_state=current_power_state, - vm_state=vm_states.ACTIVE, + power_state=new_power_state, + vm_state=new_vm_state, task_state=None) except exception.InstanceNotFound: LOG.warn(_("Instance disappeared during reboot"), diff --git a/nova/tests/compute/test_compute.py b/nova/tests/compute/test_compute.py index f39230ce7..bf6508803 100644 --- a/nova/tests/compute/test_compute.py +++ b/nova/tests/compute/test_compute.py @@ -1537,7 +1537,8 @@ class ComputeTestCase(BaseTestCase): instance=jsonutils.to_primitive(instance)) def _test_reboot(self, soft, legacy_nwinfo_driver, - test_delete=False, test_unrescue=False): + test_delete=False, test_unrescue=False, + fail_reboot=False, fail_running=False): # This is a true unit test, so we don't need the network stubs. fake_network.unset_stub_network_methods(self.stubs) @@ -1550,11 +1551,16 @@ class ComputeTestCase(BaseTestCase): instance = dict(uuid='fake-instance', power_state='unknown', - vm_state=vm_states.ACTIVE) + vm_state=vm_states.ACTIVE, + launched_at=timeutils.utcnow()) updated_instance1 = dict(uuid='updated-instance1', - power_state='fake') + power_state='fake', + vm_state=vm_states.ACTIVE, + launched_at=timeutils.utcnow()) updated_instance2 = dict(uuid='updated-instance2', - power_state='fake') + power_state='fake', + vm_state=vm_states.ACTIVE, + launched_at=timeutils.utcnow()) if test_unrescue: instance['vm_state'] = vm_states.RESCUED @@ -1564,7 +1570,8 @@ class ComputeTestCase(BaseTestCase): fake_block_dev_info = 'fake_block_dev_info' fake_power_state1 = 'fake_power_state1' - fake_power_state2 = 'fake_power_state2' + fake_power_state2 = power_state.RUNNING + fake_power_state3 = 'fake_power_state3' reboot_type = soft and 'SOFT' or 'HARD' # Beginning of calls we expect. @@ -1587,8 +1594,7 @@ class ComputeTestCase(BaseTestCase): self.compute._get_power_state(econtext, instance).AndReturn(fake_power_state1) self.compute._instance_update(econtext, instance['uuid'], - power_state=fake_power_state1, - vm_state=instance['vm_state']).AndReturn(updated_instance1) + power_state=fake_power_state1).AndReturn(updated_instance1) # Reboot should check the driver to see if legacy nwinfo is # needed. If it is, the model's legacy() method should be @@ -1619,15 +1625,24 @@ class ComputeTestCase(BaseTestCase): # within `reboot_instance`, we don't have access to its value and # can't stub it out, thus we skip that comparison. kwargs.pop('bad_volumes_callback') + if fail_reboot: + raise exception.InstanceNotFound(instance_id='instance-0000') self.stubs.Set(self.compute.driver, 'reboot', fake_reboot) # Power state should be updated again - self.compute._get_power_state(econtext, - updated_instance1).AndReturn(fake_power_state2) + if not fail_reboot or fail_running: + new_power_state = fake_power_state2 + self.compute._get_power_state(econtext, + updated_instance1).AndReturn(fake_power_state2) + else: + new_power_state = fake_power_state3 + self.compute._get_power_state(econtext, + updated_instance1).AndReturn(fake_power_state3) + if test_delete: self.compute._instance_update(econtext, updated_instance1['uuid'], - power_state=fake_power_state2, + power_state=new_power_state, task_state=None, vm_state=vm_states.ACTIVE).AndRaise( exception.InstanceNotFound( @@ -1635,9 +1650,15 @@ class ComputeTestCase(BaseTestCase): self.compute._notify_about_instance_usage(econtext, updated_instance1, 'reboot.end') + elif fail_reboot and not fail_running: + self.compute._instance_update(econtext, updated_instance1['uuid'], + vm_state=vm_states.ERROR).AndRaise( + exception.InstanceNotFound( + instance_id=updated_instance1['uuid'])) + else: self.compute._instance_update(econtext, updated_instance1['uuid'], - power_state=fake_power_state2, + power_state=new_power_state, task_state=None, vm_state=vm_states.ACTIVE).AndReturn(updated_instance2) self.compute._notify_about_instance_usage(econtext, @@ -1645,9 +1666,18 @@ class ComputeTestCase(BaseTestCase): 'reboot.end') self.mox.ReplayAll() - self.compute.reboot_instance(self.context, instance=instance, - block_device_info=fake_block_dev_info, - reboot_type=reboot_type) + + if not fail_reboot or fail_running: + self.compute.reboot_instance(self.context, instance=instance, + block_device_info=fake_block_dev_info, + reboot_type=reboot_type) + else: + self.assertRaises(exception.InstanceNotFound, + self.compute.reboot_instance, + self.context, instance=instance, + block_device_info=fake_block_dev_info, + reboot_type=reboot_type) + self.assertEqual(expected_call_info, reboot_call_info) def test_reboot_soft(self): @@ -1680,6 +1710,13 @@ class ComputeTestCase(BaseTestCase): def test_reboot_hard_legacy_nwinfo_driver(self): self._test_reboot(False, True) + def test_reboot_fail(self): + self._test_reboot(False, False, fail_reboot=True) + + def test_reboot_fail_running(self): + self._test_reboot(False, False, fail_reboot=True, + fail_running=True) + def test_set_admin_password(self): # Ensure instance can have its admin password set. instance = jsonutils.to_primitive(self._create_fake_instance()) |