From 3909e933011d8bf800c967f4be044e140a055bf4 Mon Sep 17 00:00:00 2001 From: waf Date: Wed, 21 May 2025 16:52:24 +0900 Subject: [PATCH] evacuate: respect original SHUTOFF state in --wait completion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When running `openstack server evacuate --wait`, the command would hang indefinitely if the instance was originally in SHUTOFF state, because only “ACTIVE” was treated as a successful completion. We now capture the server’s status before evacuation and dynamically include “SHUTOFF” in the `success_status` list if the instance was already shut off. This ensures that a shutoff instance is accepted as a valid completion without requiring manual intervention. Unit tests have been added and updated to cover both: - pre-evacuation ACTIVE → success_status=['active'] - pre-evacuation SHUTOFF → success_status=['active','shutoff'] Closes-Bug: #2103426 Change-Id: I86ad1cd173a144b16fde1dbac87819fab2d7a50a --- openstackclient/compute/v2/server.py | 6 ++++ .../tests/unit/compute/v2/test_server.py | 28 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 4260e7888c..cafd672f3a 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -3879,9 +3879,15 @@ host.""" compute_client.evacuate_server(server, **kwargs) if parsed_args.wait: + orig_status = server.status + success = ['ACTIVE'] + if orig_status == 'SHUTOFF': + success.append('SHUTOFF') + if utils.wait_for_status( compute_client.get_server, server.id, + success_status=success, callback=_show_progress, ): self.app.stdout.write(_('Complete\n')) diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index eb0b934e39..8ee61d17b3 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -7000,6 +7000,7 @@ class TestServerEvacuate(TestServer): 'image': self.image, 'networks': {}, 'adminPass': 'passw0rd', + 'status': 'ACTIVE', } self.server = compute_fakes.create_one_server(attrs=attrs) attrs['id'] = self.server.id @@ -7137,6 +7138,33 @@ class TestServerEvacuate(TestServer): mock_wait_for_status.assert_called_once_with( self.compute_client.get_server, self.server.id, + success_status=['ACTIVE'], + callback=mock.ANY, + ) + + @mock.patch.object(common_utils, 'wait_for_status', return_value=True) + def test_evacuate_with_wait_ok_shutoff(self, mock_wait_for_status): + self.server.status = 'SHUTOFF' + self.compute_client.get_server.return_value = self.server + + args = [ + self.server.id, + '--wait', + ] + verify_args = [ + ('server', self.server.id), + ('wait', True), + ] + evac_args = { + 'host': None, + 'on_shared_storage': False, + 'admin_pass': None, + } + self._test_evacuate(args, verify_args, evac_args) + mock_wait_for_status.assert_called_once_with( + self.compute_client.get_server, + self.server.id, + success_status=['ACTIVE', 'SHUTOFF'], callback=mock.ANY, )