Skip to content

Commit ac6f895

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "compute: enhance compute evacuate instance to support target state"
2 parents e9d716f + 8c2e765 commit ac6f895

File tree

15 files changed

+201
-65
lines changed

15 files changed

+201
-65
lines changed

nova/compute/api.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3797,7 +3797,8 @@ def _reset_image_metadata():
37973797
orig_sys_metadata=orig_sys_metadata, bdms=bdms,
37983798
preserve_ephemeral=preserve_ephemeral, host=host,
37993799
request_spec=request_spec,
3800-
reimage_boot_volume=reimage_boot_volume)
3800+
reimage_boot_volume=reimage_boot_volume,
3801+
target_state=None)
38013802

38023803
def _check_volume_status(self, context, bdms):
38033804
"""Check whether the status of the volume is "in-use".
@@ -5617,7 +5618,7 @@ def live_migrate_abort(self, context, instance, migration_id,
56175618
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED,
56185619
vm_states.ERROR], task_state=None)
56195620
def evacuate(self, context, instance, host, on_shared_storage,
5620-
admin_password=None, force=None):
5621+
admin_password=None, force=None, target_state=None):
56215622
"""Running evacuate to target host.
56225623
56235624
Checking vm compute host state, if the host not in expected_state,
@@ -5628,6 +5629,7 @@ def evacuate(self, context, instance, host, on_shared_storage,
56285629
:param on_shared_storage: True if instance files on shared storage
56295630
:param admin_password: password to set on rebuilt instance
56305631
:param force: Force the evacuation to the specific host target
5632+
:param target_state: Set a target state for the evacuated instance
56315633
56325634
"""
56335635
LOG.debug('vm evacuation scheduled', instance=instance)
@@ -5691,7 +5693,7 @@ def evacuate(self, context, instance, host, on_shared_storage,
56915693
on_shared_storage=on_shared_storage,
56925694
host=host,
56935695
request_spec=request_spec,
5694-
)
5696+
target_state=target_state)
56955697

56965698
def get_migrations(self, context, filters):
56975699
"""Get all migrations for the given filters."""

nova/compute/manager.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -618,7 +618,7 @@ def update_compute_provider_status(self, context, rp_uuid, enabled):
618618
class ComputeManager(manager.Manager):
619619
"""Manages the running instances from creation to destruction."""
620620

621-
target = messaging.Target(version='6.1')
621+
target = messaging.Target(version='6.2')
622622

623623
def __init__(self, compute_driver=None, *args, **kwargs):
624624
"""Load configuration options and connect to the hypervisor."""
@@ -3696,7 +3696,7 @@ def rebuild_instance(self, context, instance, orig_image_ref, image_ref,
36963696
bdms, recreate, on_shared_storage,
36973697
preserve_ephemeral, migration,
36983698
scheduled_node, limits, request_spec, accel_uuids,
3699-
reimage_boot_volume):
3699+
reimage_boot_volume, target_state):
37003700
"""Destroy and re-make this instance.
37013701

37023702
A 'rebuild' effectively purges all existing data from the system and
@@ -3731,6 +3731,7 @@ def rebuild_instance(self, context, instance, orig_image_ref, image_ref,
37313731
:param reimage_boot_volume: Boolean to specify whether the user has
37323732
explicitly requested to rebuild a boot
37333733
volume
3734+
:param target_state: Set a target state for the evacuated instance.
37343735

37353736
"""
37363737
# recreate=True means the instance is being evacuated from a failed
@@ -3795,7 +3796,8 @@ def rebuild_instance(self, context, instance, orig_image_ref, image_ref,
37953796
image_meta, injected_files, new_pass, orig_sys_metadata,
37963797
bdms, evacuate, on_shared_storage, preserve_ephemeral,
37973798
migration, request_spec, allocs, rebuild_claim,
3798-
scheduled_node, limits, accel_uuids, reimage_boot_volume)
3799+
scheduled_node, limits, accel_uuids, reimage_boot_volume,
3800+
target_state)
37993801
except (exception.ComputeResourcesUnavailable,
38003802
exception.RescheduledException) as e:
38013803
if isinstance(e, exception.ComputeResourcesUnavailable):
@@ -3855,7 +3857,7 @@ def _do_rebuild_instance_with_claim(
38553857
injected_files, new_pass, orig_sys_metadata, bdms, evacuate,
38563858
on_shared_storage, preserve_ephemeral, migration, request_spec,
38573859
allocations, rebuild_claim, scheduled_node, limits, accel_uuids,
3858-
reimage_boot_volume):
3860+
reimage_boot_volume, target_state):
38593861
"""Helper to avoid deep nesting in the top-level method."""
38603862

38613863
provider_mapping = None
@@ -3879,7 +3881,8 @@ def _do_rebuild_instance_with_claim(
38793881
context, instance, orig_image_ref, image_meta, injected_files,
38803882
new_pass, orig_sys_metadata, bdms, evacuate, on_shared_storage,
38813883
preserve_ephemeral, migration, request_spec, allocations,
3882-
provider_mapping, accel_uuids, reimage_boot_volume)
3884+
provider_mapping, accel_uuids, reimage_boot_volume,
3885+
target_state)
38833886

38843887
@staticmethod
38853888
def _get_image_name(image_meta):
@@ -3893,10 +3896,18 @@ def _do_rebuild_instance(
38933896
injected_files, new_pass, orig_sys_metadata, bdms, evacuate,
38943897
on_shared_storage, preserve_ephemeral, migration, request_spec,
38953898
allocations, request_group_resource_providers_mapping,
3896-
accel_uuids, reimage_boot_volume):
3899+
accel_uuids, reimage_boot_volume, target_state):
38973900
orig_vm_state = instance.vm_state
38983901

38993902
if evacuate:
3903+
if target_state and orig_vm_state != vm_states.ERROR:
3904+
# This will ensure that at destination the instance will have
3905+
# the desired state.
3906+
if target_state not in vm_states.ALLOW_TARGET_STATES:
3907+
raise exception.InstanceEvacuateNotSupportedTargetState(
3908+
target_state=target_state)
3909+
orig_vm_state = target_state
3910+
39003911
if request_spec:
39013912
# NOTE(gibi): Do a late check of server group policy as
39023913
# parallel scheduling could violate such policy. This will
@@ -11369,7 +11380,7 @@ def rebuild_instance(self, context, instance, orig_image_ref, image_ref,
1136911380
bdms, recreate, on_shared_storage,
1137011381
preserve_ephemeral, migration,
1137111382
scheduled_node, limits, request_spec,
11372-
accel_uuids, False)
11383+
accel_uuids, False, None)
1137311384

1137411385
# 5.13 support for optional accel_uuids argument
1137511386
def shelve_instance(self, context, instance, image_id,

nova/compute/rpcapi.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,7 @@ class ComputeAPI(object):
403403
* ... - Rename the instance_type argument of resize_instance() to
404404
flavor
405405
* 6.1 - Add reimage_boot_volume parameter to rebuild_instance()
406+
* 6.2 - Add target_state parameter to rebuild_instance()
406407
'''
407408

408409
VERSION_ALIASES = {
@@ -424,6 +425,7 @@ class ComputeAPI(object):
424425
'xena': '6.0',
425426
'yoga': '6.0',
426427
'zed': '6.1',
428+
'antilope': '6.2',
427429
}
428430

429431
@property
@@ -1083,7 +1085,7 @@ def rebuild_instance(
10831085
image_ref, orig_image_ref, orig_sys_metadata, bdms,
10841086
recreate, on_shared_storage, host, node,
10851087
preserve_ephemeral, migration, limits, request_spec, accel_uuids,
1086-
reimage_boot_volume):
1088+
reimage_boot_volume, target_state):
10871089

10881090
# NOTE(edleafe): compute nodes can only use the dict form of limits.
10891091
if isinstance(limits, objects.SchedulerLimits):
@@ -1096,11 +1098,19 @@ def rebuild_instance(
10961098
'limits': limits,
10971099
'request_spec': request_spec,
10981100
'accel_uuids': accel_uuids,
1099-
'reimage_boot_volume': reimage_boot_volume
1101+
'reimage_boot_volume': reimage_boot_volume,
1102+
'target_state': target_state,
11001103
}
1101-
1102-
version = '6.1'
1104+
version = '6.2'
11031105
client = self.router.client(ctxt)
1106+
if not client.can_send_version(version):
1107+
if msg_args['target_state']:
1108+
raise exception.UnsupportedRPCVersion(
1109+
api="rebuild_instance",
1110+
required="6.2")
1111+
else:
1112+
del msg_args['target_state']
1113+
version = '6.1'
11041114
if not client.can_send_version(version):
11051115
if msg_args['reimage_boot_volume']:
11061116
raise exception.NovaException(

nova/compute/vm_states.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,6 @@
7676

7777
# states we allow resources to be freed in
7878
ALLOW_RESOURCE_REMOVAL = [DELETED, SHELVED_OFFLOADED]
79+
80+
# states we allow for evacuate instance
81+
ALLOW_TARGET_STATES = [STOPPED]

nova/conductor/api.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,8 @@ def rebuild_instance(self, context, instance, orig_image_ref, image_ref,
144144
injected_files, new_pass, orig_sys_metadata,
145145
bdms, recreate=False, on_shared_storage=False,
146146
preserve_ephemeral=False, host=None,
147-
request_spec=None, reimage_boot_volume=False):
147+
request_spec=None, reimage_boot_volume=False,
148+
target_state=None):
148149
self.conductor_compute_rpcapi.rebuild_instance(context,
149150
instance=instance,
150151
new_pass=new_pass,
@@ -158,7 +159,8 @@ def rebuild_instance(self, context, instance, orig_image_ref, image_ref,
158159
preserve_ephemeral=preserve_ephemeral,
159160
host=host,
160161
request_spec=request_spec,
161-
reimage_boot_volume=reimage_boot_volume)
162+
reimage_boot_volume=reimage_boot_volume,
163+
target_state=target_state)
162164

163165
def cache_images(self, context, aggregate, image_ids):
164166
"""Request images be pre-cached on hosts within an aggregate.

nova/conductor/manager.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ class ComputeTaskManager:
235235
may involve coordinating activities on multiple compute nodes.
236236
"""
237237

238-
target = messaging.Target(namespace='compute_task', version='1.24')
238+
target = messaging.Target(namespace='compute_task', version='1.25')
239239

240240
def __init__(self):
241241
self.compute_rpcapi = compute_rpcapi.ComputeAPI()
@@ -1152,7 +1152,8 @@ def rebuild_instance(self, context, instance, orig_image_ref, image_ref,
11521152
injected_files, new_pass, orig_sys_metadata,
11531153
bdms, recreate, on_shared_storage,
11541154
preserve_ephemeral=False, host=None,
1155-
request_spec=None, reimage_boot_volume=False):
1155+
request_spec=None, reimage_boot_volume=False,
1156+
target_state=None):
11561157
# recreate=True means the instance is being evacuated from a failed
11571158
# host to a new destination host. The 'recreate' variable name is
11581159
# confusing, so rename it to evacuate here at the top, which is simpler
@@ -1356,7 +1357,8 @@ def rebuild_instance(self, context, instance, orig_image_ref, image_ref,
13561357
limits=limits,
13571358
request_spec=request_spec,
13581359
accel_uuids=accel_uuids,
1359-
reimage_boot_volume=reimage_boot_volume)
1360+
reimage_boot_volume=reimage_boot_volume,
1361+
target_state=target_state)
13601362

13611363
def _validate_image_traits_for_rebuild(self, context, instance, image_ref):
13621364
"""Validates that the traits specified in the image can be satisfied

nova/conductor/rpcapi.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@ class ComputeTaskAPI(object):
287287
1.22 - Added confirm_snapshot_based_resize()
288288
1.23 - Added revert_snapshot_based_resize()
289289
1.24 - Add reimage_boot_volume parameter to rebuild_instance()
290+
1.25 - Add target_state parameter to rebuild_instance()
290291
"""
291292

292293
def __init__(self):
@@ -428,8 +429,8 @@ def rebuild_instance(self, ctxt, instance, new_pass, injected_files,
428429
image_ref, orig_image_ref, orig_sys_metadata, bdms,
429430
recreate=False, on_shared_storage=False, host=None,
430431
preserve_ephemeral=False, request_spec=None,
431-
reimage_boot_volume=False):
432-
version = '1.24'
432+
reimage_boot_volume=False, target_state=None):
433+
version = '1.25'
433434
kw = {'instance': instance,
434435
'new_pass': new_pass,
435436
'injected_files': injected_files,
@@ -442,8 +443,16 @@ def rebuild_instance(self, ctxt, instance, new_pass, injected_files,
442443
'preserve_ephemeral': preserve_ephemeral,
443444
'host': host,
444445
'request_spec': request_spec,
445-
'reimage_boot_volume': reimage_boot_volume
446+
'reimage_boot_volume': reimage_boot_volume,
447+
'target_state': target_state,
446448
}
449+
if not self.client.can_send_version(version):
450+
if kw['target_state']:
451+
raise exception.UnsupportedRPCVersion(
452+
api="rebuild_instance", required="1.25")
453+
else:
454+
del kw['target_state']
455+
version = '1.24'
447456
if not self.client.can_send_version(version):
448457
if kw['reimage_boot_volume']:
449458
raise exception.NovaException(

nova/exception.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1451,6 +1451,11 @@ class InstanceEvacuateNotSupported(Invalid):
14511451
msg_fmt = _('Instance evacuate is not supported.')
14521452

14531453

1454+
class InstanceEvacuateNotSupportedTargetState(Invalid):
1455+
msg_fmt = _("Target state '%(target_state)s' for instance evacuate "
1456+
"is not supported.")
1457+
1458+
14541459
class DBNotAllowed(NovaException):
14551460
msg_fmt = _('%(binary)s attempted direct database access which is '
14561461
'not allowed by policy')
@@ -1479,6 +1484,11 @@ class UnsupportedRescueImage(Invalid):
14791484
msg_fmt = _("Requested rescue image '%(image)s' is not supported")
14801485

14811486

1487+
class UnsupportedRPCVersion(Invalid):
1488+
msg_fmt = _("Unsupported RPC version for %(api)s. "
1489+
"Required >= %(required)s")
1490+
1491+
14821492
class Base64Exception(NovaException):
14831493
msg_fmt = _("Invalid Base 64 data for file %(path)s")
14841494

nova/objects/service.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131

3232

3333
# NOTE(danms): This is the global service version counter
34-
SERVICE_VERSION = 65
34+
SERVICE_VERSION = 66
3535

3636

3737
# NOTE(danms): This is our SERVICE_VERSION history. The idea is that any
@@ -228,6 +228,9 @@
228228
# Version 65: Compute RPC v6.1:
229229
# Added stable local node identity
230230
{'compute_rpc': '6.1'},
231+
# Version 66: Compute RPC v6.2:
232+
# Add target_state parameter to rebuild_instance()
233+
{'compute_rpc': '6.2'},
231234
)
232235

233236
# This is the version after which we can rely on having a persistent

nova/tests/functional/api_sample_tests/test_evacuate.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ def test_server_evacuate(self, rebuild_mock):
8080
orig_sys_metadata=mock.ANY, bdms=mock.ANY, recreate=mock.ANY,
8181
on_shared_storage=False, preserve_ephemeral=mock.ANY,
8282
host='testHost', request_spec=mock.ANY,
83-
reimage_boot_volume=False)
83+
reimage_boot_volume=False, target_state=None)
8484

8585
@mock.patch('nova.conductor.manager.ComputeTaskManager.rebuild_instance')
8686
def test_server_evacuate_find_host(self, rebuild_mock):
@@ -97,7 +97,7 @@ def test_server_evacuate_find_host(self, rebuild_mock):
9797
orig_sys_metadata=mock.ANY, bdms=mock.ANY, recreate=mock.ANY,
9898
on_shared_storage=False, preserve_ephemeral=mock.ANY,
9999
host=None, request_spec=mock.ANY,
100-
reimage_boot_volume=False)
100+
reimage_boot_volume=False, target_state=None)
101101

102102

103103
class EvacuateJsonTestV214(EvacuateJsonTest):
@@ -119,7 +119,7 @@ def test_server_evacuate(self, rebuild_mock):
119119
orig_sys_metadata=mock.ANY, bdms=mock.ANY, recreate=mock.ANY,
120120
on_shared_storage=None, preserve_ephemeral=mock.ANY,
121121
host='testHost', request_spec=mock.ANY,
122-
reimage_boot_volume=False)
122+
reimage_boot_volume=False, target_state=None)
123123

124124
@mock.patch('nova.conductor.manager.ComputeTaskManager.rebuild_instance')
125125
def test_server_evacuate_find_host(self, rebuild_mock):
@@ -135,7 +135,7 @@ def test_server_evacuate_find_host(self, rebuild_mock):
135135
orig_sys_metadata=mock.ANY, bdms=mock.ANY, recreate=mock.ANY,
136136
on_shared_storage=None, preserve_ephemeral=mock.ANY,
137137
host=None, request_spec=mock.ANY,
138-
reimage_boot_volume=False)
138+
reimage_boot_volume=False, target_state=None)
139139

140140

141141
class EvacuateJsonTestV229(EvacuateJsonTestV214):
@@ -163,7 +163,7 @@ def test_server_evacuate(self, compute_node_get_all_by_host, rebuild_mock):
163163
orig_sys_metadata=mock.ANY, bdms=mock.ANY, recreate=mock.ANY,
164164
on_shared_storage=None, preserve_ephemeral=mock.ANY,
165165
host=None, request_spec=mock.ANY,
166-
reimage_boot_volume=False)
166+
reimage_boot_volume=False, target_state=None)
167167

168168
@mock.patch('nova.conductor.manager.ComputeTaskManager.rebuild_instance')
169169
@mock.patch('nova.objects.ComputeNodeList.get_all_by_host')
@@ -184,7 +184,7 @@ def test_server_evacuate_with_force(self, compute_node_get_all_by_host,
184184
orig_sys_metadata=mock.ANY, bdms=mock.ANY, recreate=mock.ANY,
185185
on_shared_storage=None, preserve_ephemeral=mock.ANY,
186186
host='testHost', request_spec=mock.ANY,
187-
reimage_boot_volume=False)
187+
reimage_boot_volume=False, target_state=None)
188188

189189

190190
class EvacuateJsonTestV268(EvacuateJsonTestV229):
@@ -211,7 +211,7 @@ def test_server_evacuate(self, compute_node_get_all_by_host, rebuild_mock):
211211
orig_sys_metadata=mock.ANY, bdms=mock.ANY, recreate=mock.ANY,
212212
on_shared_storage=None, preserve_ephemeral=mock.ANY,
213213
host=None, request_spec=mock.ANY,
214-
reimage_boot_volume=False)
214+
reimage_boot_volume=False, target_state=None)
215215

216216
def test_server_evacuate_with_force(self):
217217
# doesn't apply to v2.68+, which removed the ability to force migrate

0 commit comments

Comments
 (0)