From a97de51e017c9c07eaa3e4a9ddde4193e9528373 Mon Sep 17 00:00:00 2001 From: Chris Behrens Date: Sat, 7 Jul 2012 21:38:36 -0700 Subject: Make reboot work for halted xenapi instances Fixes bug 1022199 This also will catch exceptions and make sure to reset the task_state, while still generating an instance fault. Change-Id: I122a1422b8e5731bc484414736ab44e60d4c9830 --- nova/compute/manager.py | 11 +++++++++-- nova/tests/test_xenapi.py | 35 +++++++++++++++++++++++++++++++++++ nova/virt/xenapi/fake.py | 12 +++++++++++- nova/virt/xenapi/vmops.py | 18 ++++++++++++++---- 4 files changed, 69 insertions(+), 7 deletions(-) (limited to 'nova') diff --git a/nova/compute/manager.py b/nova/compute/manager.py index afd422253..aacfdf7a7 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -959,8 +959,15 @@ class ComputeManager(manager.SchedulerDependentManager): context=context, instance_uuid=instance_uuid) network_info = self._get_instance_nw_info(context, instance) - self.driver.reboot(instance, self._legacy_nw_info(network_info), - reboot_type) + try: + self.driver.reboot(instance, self._legacy_nw_info(network_info), + reboot_type) + except Exception, exc: + LOG.error(_('Cannot reboot instance: %(exc)s'), locals(), + context=context, instance_uuid=instance_uuid) + self.add_instance_fault_from_exc(context, instance_uuid, exc, + sys.exc_info()) + # Fall through and reset task_state to None current_power_state = self._get_power_state(context, instance) self._instance_update(context, diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 3138ef0b9..aeacf1fa6 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -807,6 +807,33 @@ class XenAPIVMTestCase(stubs.XenAPITestBase): conn.finish_revert_migration(instance, None) self.assertTrue(conn._vmops.finish_revert_migration_called) + def test_reboot_hard(self): + instance = self._create_instance() + conn = xenapi_conn.XenAPIDriver(False) + conn.reboot(instance, None, "HARD") + + def test_reboot_soft(self): + instance = self._create_instance() + conn = xenapi_conn.XenAPIDriver(False) + conn.reboot(instance, None, "SOFT") + + def test_reboot_halted(self): + session = xenapi_conn.XenAPISession('test_url', 'root', 'test_pass') + instance = self._create_instance(spawn=False) + conn = xenapi_conn.XenAPIDriver(False) + xenapi_fake.create_vm(instance.name, 'Halted') + conn.reboot(instance, None, "SOFT") + vm_ref = vm_utils.lookup(session, instance.name) + vm = xenapi_fake.get_record('VM', vm_ref) + self.assertEquals(vm['power_state'], 'Running') + + def test_reboot_unknown_state(self): + instance = self._create_instance(spawn=False) + conn = xenapi_conn.XenAPIDriver(False) + xenapi_fake.create_vm(instance.name, 'Unknown') + self.assertRaises(xenapi_fake.Failure, conn.reboot, instance, + None, "SOFT") + def _create_instance(self, instance_id=1, spawn=True): """Creates and spawns a test instance.""" instance_values = { @@ -938,6 +965,14 @@ class XenAPIMigrateInstance(stubs.XenAPITestBase): conn.migrate_disk_and_power_off(self.context, instance, '127.0.0.1', instance_type, None) + def test_migrate_disk_and_power_off(self): + instance = db.instance_create(self.context, self.instance_values) + xenapi_fake.create_vm(instance.name, 'Running') + instance_type = db.instance_type_get_by_name(self.context, 'm1.large') + conn = xenapi_conn.XenAPIDriver(False) + conn.migrate_disk_and_power_off(self.context, instance, + '127.0.0.1', instance_type, None) + def test_migrate_disk_and_power_off_passes_exceptions(self): instance = db.instance_create(self.context, self.instance_values) xenapi_fake.create_vm(instance.name, 'Running') diff --git a/nova/virt/xenapi/fake.py b/nova/virt/xenapi/fake.py index 72008a69d..050b696f0 100644 --- a/nova/virt/xenapi/fake.py +++ b/nova/virt/xenapi/fake.py @@ -536,8 +536,18 @@ class SessionBase(object): VDI_resize = VDI_resize_online + def _VM_reboot(self, session, vm_ref): + db_ref = _db_content['VM'][vm_ref] + if db_ref['power_state'] != 'Running': + raise Failure(['VM_BAD_POWER_STATE', + 'fake-opaque-ref', db_ref['power_state'].lower(), 'halted']) + db_ref['power_state'] = 'Running' + def VM_clean_reboot(self, session, vm_ref): - pass + return self._VM_reboot(session, vm_ref) + + def VM_hard_reboot(self, session, vm_ref): + return self._VM_reboot(session, vm_ref) def VM_hard_shutdown(self, session, vm_ref): db_ref = _db_content['VM'][vm_ref] diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 6af2ac9e0..5d445e0a6 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -843,10 +843,20 @@ class VMOps(object): # remove existing filters vm_ref = self._get_vm_opaque_ref(instance) - if reboot_type == "HARD": - self._session.call_xenapi('VM.hard_reboot', vm_ref) - else: - self._session.call_xenapi('VM.clean_reboot', vm_ref) + try: + if reboot_type == "HARD": + self._session.call_xenapi('VM.hard_reboot', vm_ref) + else: + self._session.call_xenapi('VM.clean_reboot', vm_ref) + except self._session.XenAPI.Failure, exc: + details = exc.details + if (details[0] == 'VM_BAD_POWER_STATE' and + details[-1] == 'halted'): + LOG.info(_("Starting halted instance found during reboot"), + instance=instance) + self._session.call_xenapi('VM.start', vm_ref, False, False) + return + raise def _get_agent_version(self, instance): """Get the version of the agent running on the VM instance.""" -- cgit