Skip to content

Commit c0ade88

Browse files
committed
Merge remote-tracking branch 'origin/stable/victoria' into stackhpc/victora-ironic-rebalance
2 parents 31fd81c + 3224ceb commit c0ade88

File tree

6 files changed

+141
-8
lines changed

6 files changed

+141
-8
lines changed

doc/source/admin/evacuate.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,17 @@ instances up and running.
9797
using a pattern you might want to use the ``--strict`` flag which got introduced
9898
in version 10.2.0 to make sure nova matches the ``FAILED_HOST``
9999
exactly.
100+
101+
.. note::
102+
.. code-block:: bash
103+
104+
+------+--------+--------------+
105+
| Name | Status | Task State |
106+
+------+--------+--------------+
107+
| vm_1 | ACTIVE | powering-off |
108+
+------------------------------+
109+
110+
If the instance task state is not None, evacuation will be possible. However,
111+
depending on the ongoing operation, there may be clean up required in other
112+
services which the instance was using, such as neutron, cinder, glance, or
113+
the storage backend.

nova/compute/api.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5185,7 +5185,7 @@ def live_migrate_abort(self, context, instance, migration_id,
51855185
@reject_vtpm_instances(instance_actions.EVACUATE)
51865186
@block_accelerators(until_service=SUPPORT_ACCELERATOR_SERVICE_FOR_REBUILD)
51875187
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED,
5188-
vm_states.ERROR])
5188+
vm_states.ERROR], task_state=None)
51895189
def evacuate(self, context, instance, host, on_shared_storage,
51905190
admin_password=None, force=None):
51915191
"""Running evacuate to target host.
@@ -5212,7 +5212,7 @@ def evacuate(self, context, instance, host, on_shared_storage,
52125212
context, instance.uuid)
52135213

52145214
instance.task_state = task_states.REBUILDING
5215-
instance.save(expected_task_state=[None])
5215+
instance.save(expected_task_state=None)
52165216
self._record_action_start(context, instance, instance_actions.EVACUATE)
52175217

52185218
# NOTE(danms): Create this as a tombstone for the source compute

nova/tests/functional/integrated_helpers.py

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,12 @@ def router(self):
9494
return rpc.ClientRouter(default_client)
9595

9696

97+
# placeholder used as a default parameter value to distinguish between the case
98+
# when the parameter is specified by the caller with None from the case when it
99+
# was not specified
100+
NOT_SPECIFIED = object()
101+
102+
97103
class InstanceHelperMixin:
98104

99105
def _wait_for_server_parameter(
@@ -493,12 +499,42 @@ def _unshelve_server(self, server, expected_state='ACTIVE'):
493499
self.api.post_server_action(server['id'], {'unshelve': {}})
494500
return self._wait_for_state_change(server, expected_state)
495501

496-
def _evacuate_server(self, server, host, expected_state='ACTIVE'):
502+
def _evacuate_server(
503+
self, server, extra_post_args=None, expected_host=None,
504+
expected_state='ACTIVE', expected_task_state=NOT_SPECIFIED,
505+
expected_migration_status='done'):
497506
"""Evacuate a server."""
498-
self.api.post_server_action(server['id'], {'evacuate': {}})
499-
self._wait_for_server_parameter(
500-
self.server, {'OS-EXT-SRV-ATTR:host': host,
501-
'status': expected_state})
507+
api = getattr(self, 'admin_api', self.api)
508+
509+
post = {'evacuate': {}}
510+
if extra_post_args:
511+
post['evacuate'].update(extra_post_args)
512+
513+
expected_result = {'status': expected_state}
514+
if expected_host:
515+
expected_result['OS-EXT-SRV-ATTR:host'] = expected_host
516+
if expected_task_state is not NOT_SPECIFIED:
517+
expected_result['OS-EXT-STS:task_state'] = expected_task_state
518+
519+
api.post_server_action(server['id'], post)
520+
521+
# NOTE(gibi): The order of waiting for the migration and returning
522+
# a fresh server from _wait_for_server_parameter is important as
523+
# the compute manager sets status of the instance before sets the
524+
# host and finally sets the migration status. So waiting for the
525+
# migration first makes the returned server object more consistent.
526+
self._wait_for_migration_status(server, [expected_migration_status])
527+
return self._wait_for_server_parameter(server, expected_result)
528+
529+
def _start_server(self, server):
530+
self.api.post_server_action(server['id'], {'os-start': None})
531+
return self._wait_for_state_change(server, 'ACTIVE')
532+
533+
def _stop_server(self, server, wait_for_stop=True):
534+
self.api.post_server_action(server['id'], {'os-stop': None})
535+
if wait_for_stop:
536+
return self._wait_for_state_change(server, 'SHUTOFF')
537+
return server
502538

503539

504540
class PlacementHelperMixin:
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# Copyright 2022 Red Hat, Inc.
2+
# All Rights Reserved.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
5+
# not use this file except in compliance with the License. You may obtain
6+
# a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
# License for the specific language governing permissions and limitations
14+
# under the License.
15+
16+
from nova import test
17+
from nova.tests import fixtures as nova_fixtures
18+
from nova.tests.functional import fixtures as func_fixtures
19+
from nova.tests.functional import integrated_helpers
20+
21+
22+
class EvacuateServerWithTaskState(
23+
test.TestCase, integrated_helpers.InstanceHelperMixin,
24+
):
25+
"""Regression test for bug 1978983
26+
If instance task state is powering-off or not None
27+
instance should be allowed to evacuate.
28+
"""
29+
30+
def setUp(self):
31+
super().setUp()
32+
# Stub out external dependencies.
33+
self.useFixture(nova_fixtures.NeutronFixture(self))
34+
self.useFixture(nova_fixtures.GlanceFixture(self))
35+
self.useFixture(func_fixtures.PlacementFixture())
36+
self.useFixture(nova_fixtures.HostNameWeigherFixture())
37+
38+
# Start nova controller services.
39+
self.start_service('conductor')
40+
self.start_service('scheduler')
41+
42+
api_fixture = self.useFixture(nova_fixtures.OSAPIFixture(
43+
api_version='v2.1'))
44+
self.api = api_fixture.admin_api
45+
self.api.microversion = 'latest'
46+
47+
self.src = self._start_compute(host='host1')
48+
self.dest = self._start_compute(host='host2')
49+
50+
def test_evacuate_instance(self):
51+
"""Evacuating a server
52+
"""
53+
server = self._create_server(networks=[])
54+
55+
server = self._wait_for_state_change(server, 'ACTIVE')
56+
self.assertEqual(self.src.host, server['OS-EXT-SRV-ATTR:host'])
57+
58+
# stop host1 compute service
59+
self.src.stop()
60+
self.api.put_service_force_down(self.src.service_ref.uuid, True)
61+
62+
# poweroff instance
63+
self._stop_server(server, wait_for_stop=False)
64+
server = self._wait_for_server_parameter(
65+
server, {'OS-EXT-STS:task_state': 'powering-off'})
66+
67+
# evacuate instance
68+
server = self._evacuate_server(
69+
server, expected_host=self.dest.host
70+
)
71+
self.assertEqual(self.dest.host, server['OS-EXT-SRV-ATTR:host'])

nova/tests/functional/test_servers.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8181,7 +8181,8 @@ def test_evacuate_ok(self):
81818181
arqs = self.cyborg.fake_get_arqs_for_instance(self.server['id'])
81828182
compute_to_stop, compute_to_evacuate = self._test_evacuate(
81838183
self.server, self.NUM_HOSTS)
8184-
self._evacuate_server(self.server, compute_to_evacuate.host)
8184+
self._evacuate_server(self.server,
8185+
expected_host=compute_to_evacuate.host)
81858186
compute_to_stop.start()
81868187
self.server = self.api.get_server(self.server['id'])
81878188
arqs_new = self.cyborg.fake_get_arqs_for_instance(self.server['id'])
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
fixes:
3+
- |
4+
If compute service is down in source node and user try to stop
5+
instance, instance gets stuck at powering-off, hence evacuation fails with
6+
msg: Cannot 'evacuate' instance <instance-id> while it is in
7+
task_state powering-off.
8+
It is now possible for evacuation to ignore the vm task state.
9+
For more details see: `bug 1978983`_
10+
11+
.. _`bug 1978983`: https://bugs.launchpad.net/nova/+bug/1978983

0 commit comments

Comments
 (0)