From e0534442b531c3f89c9c19d95491dcddd5b0fe41 Mon Sep 17 00:00:00 2001 From: LeopardMa Date: Wed, 7 Feb 2018 10:21:08 +0800 Subject: [PATCH] Fix shelving a paused instance It is possible to shelve a paused instance, but in that case the guest is already shutdown, and some hypervisors will fail when trying to perform a clean shutdown of a non-running guest. For example, attempting to shelve a paused libvirt instance will result in this error: libvirtError: Requested operation is not valid: domain is not running Therefore, if the instance is paused, we don't attempt a clean shutdown while shelving. Related Tempest test: https://review.openstack.org/564127/ Closes-Bug: #1745529 Change-Id: I8ca25d9847d50001fbe8513a6c1dba8b697c24e4 (cherry picked from commit d5901f613cf98f61b5253a1568b22af1d9dd1a08) --- nova/compute/manager.py | 6 +++++- nova/tests/unit/compute/test_shelve.py | 19 ++++++++++++++----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 6181bc3a3d9c..30d4d5647840 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -4323,7 +4323,11 @@ class ComputeManager(manager.Manager): expected_state = shelving_state_map[expected_state] instance.task_state = task_state instance.save(expected_task_state=expected_state) - + # Do not attempt a clean shutdown of a paused guest since some + # hypervisors will fail the clean shutdown if the guest is not + # running. + if instance.power_state == power_state.PAUSED: + clean_shutdown = False self._power_off_instance(context, instance, clean_shutdown) self.driver.snapshot(context, instance, image_id, update_task_state) diff --git a/nova/tests/unit/compute/test_shelve.py b/nova/tests/unit/compute/test_shelve.py index 8c74faec2c00..2f2d7af0f2ea 100644 --- a/nova/tests/unit/compute/test_shelve.py +++ b/nova/tests/unit/compute/test_shelve.py @@ -16,6 +16,7 @@ from oslo_utils import fixture as utils_fixture from oslo_utils import timeutils from nova.compute import claims +from nova.compute import power_state from nova.compute import task_states from nova.compute import vm_states import nova.conf @@ -55,12 +56,14 @@ class ShelveComputeManagerTestCase(test_compute.BaseTestCase): def _shelve_instance(self, shelved_offload_time, mock_notify, mock_notify_instance_usage, mock_get_power_state, mock_snapshot, mock_power_off, mock_terminate, - clean_shutdown=True): + clean_shutdown=True, + guest_power_state=power_state.RUNNING): mock_get_power_state.return_value = 123 CONF.set_override('shelved_offload_time', shelved_offload_time) host = 'fake-mini' - instance = self._create_fake_instance_obj(params={'host': host}) + instance = self._create_fake_instance_obj( + params={'host': host, 'power_state': guest_power_state}) image_id = 'fake_image_id' host = 'fake-mini' self.useFixture(utils_fixture.TimeFixture()) @@ -130,9 +133,12 @@ class ShelveComputeManagerTestCase(test_compute.BaseTestCase): mock_cleanup_call_list = [] if clean_shutdown: - mock_power_off_call_list.append( - mock.call(instance, CONF.shutdown_timeout, - self.compute.SHUTDOWN_RETRY_INTERVAL)) + if guest_power_state == power_state.PAUSED: + mock_power_off_call_list.append(mock.call(instance, 0, 0)) + else: + mock_power_off_call_list.append( + mock.call(instance, CONF.shutdown_timeout, + self.compute.SHUTDOWN_RETRY_INTERVAL)) else: mock_power_off_call_list.append(mock.call(instance, 0, 0)) @@ -170,6 +176,9 @@ class ShelveComputeManagerTestCase(test_compute.BaseTestCase): def test_shelve_and_offload(self): self._shelve_instance(0) + def test_shelve_paused_instance(self): + self._shelve_instance(-1, guest_power_state=power_state.PAUSED) + @mock.patch.object(nova.virt.fake.SmallFakeDriver, 'power_off') def test_shelve_offload(self, mock_power_off): instance = self._shelve_offload()