Skip to content

Commit 8c2e765

Browse files
committed
compute: enhance compute evacuate instance to support target state
Related to the bp/allowing-target-state-for-evacuate. This change is extending compute API to accept a new argument targetState. The targetState argument when set will force state of an evacuated instance to the destination host. Signed-off-by: Sahid Orentino Ferdjaoui <[email protected]> Change-Id: I9660d42937ad62d647afc6be965f166cc5631392
1 parent 23c5f3d commit 8c2e765

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."""
@@ -3674,7 +3674,7 @@ def rebuild_instance(self, context, instance, orig_image_ref, image_ref,
36743674
bdms, recreate, on_shared_storage,
36753675
preserve_ephemeral, migration,
36763676
scheduled_node, limits, request_spec, accel_uuids,
3677-
reimage_boot_volume):
3677+
reimage_boot_volume, target_state):
36783678
"""Destroy and re-make this instance.
36793679

36803680
A 'rebuild' effectively purges all existing data from the system and
@@ -3709,6 +3709,7 @@ def rebuild_instance(self, context, instance, orig_image_ref, image_ref,
37093709
:param reimage_boot_volume: Boolean to specify whether the user has
37103710
explicitly requested to rebuild a boot
37113711
volume
3712+
:param target_state: Set a target state for the evacuated instance.
37123713

37133714
"""
37143715
# recreate=True means the instance is being evacuated from a failed
@@ -3773,7 +3774,8 @@ def rebuild_instance(self, context, instance, orig_image_ref, image_ref,
37733774
image_meta, injected_files, new_pass, orig_sys_metadata,
37743775
bdms, evacuate, on_shared_storage, preserve_ephemeral,
37753776
migration, request_spec, allocs, rebuild_claim,
3776-
scheduled_node, limits, accel_uuids, reimage_boot_volume)
3777+
scheduled_node, limits, accel_uuids, reimage_boot_volume,
3778+
target_state)
37773779
except (exception.ComputeResourcesUnavailable,
37783780
exception.RescheduledException) as e:
37793781
if isinstance(e, exception.ComputeResourcesUnavailable):
@@ -3833,7 +3835,7 @@ def _do_rebuild_instance_with_claim(
38333835
injected_files, new_pass, orig_sys_metadata, bdms, evacuate,
38343836
on_shared_storage, preserve_ephemeral, migration, request_spec,
38353837
allocations, rebuild_claim, scheduled_node, limits, accel_uuids,
3836-
reimage_boot_volume):
3838+
reimage_boot_volume, target_state):
38373839
"""Helper to avoid deep nesting in the top-level method."""
38383840

38393841
provider_mapping = None
@@ -3857,7 +3859,8 @@ def _do_rebuild_instance_with_claim(
38573859
context, instance, orig_image_ref, image_meta, injected_files,
38583860
new_pass, orig_sys_metadata, bdms, evacuate, on_shared_storage,
38593861
preserve_ephemeral, migration, request_spec, allocations,
3860-
provider_mapping, accel_uuids, reimage_boot_volume)
3862+
provider_mapping, accel_uuids, reimage_boot_volume,
3863+
target_state)
38613864

38623865
@staticmethod
38633866
def _get_image_name(image_meta):
@@ -3871,10 +3874,18 @@ def _do_rebuild_instance(
38713874
injected_files, new_pass, orig_sys_metadata, bdms, evacuate,
38723875
on_shared_storage, preserve_ephemeral, migration, request_spec,
38733876
allocations, request_group_resource_providers_mapping,
3874-
accel_uuids, reimage_boot_volume):
3877+
accel_uuids, reimage_boot_volume, target_state):
38753878
orig_vm_state = instance.vm_state
38763879

38773880
if evacuate:
3881+
if target_state and orig_vm_state != vm_states.ERROR:
3882+
# This will ensure that at destination the instance will have
3883+
# the desired state.
3884+
if target_state not in vm_states.ALLOW_TARGET_STATES:
3885+
raise exception.InstanceEvacuateNotSupportedTargetState(
3886+
target_state=target_state)
3887+
orig_vm_state = target_state
3888+
38783889
if request_spec:
38793890
# NOTE(gibi): Do a late check of server group policy as
38803891
# parallel scheduling could violate such policy. This will
@@ -11347,7 +11358,7 @@ def rebuild_instance(self, context, instance, orig_image_ref, image_ref,
1134711358
bdms, recreate, on_shared_storage,
1134811359
preserve_ephemeral, migration,
1134911360
scheduled_node, limits, request_spec,
11350-
accel_uuids, False)
11361+
accel_uuids, False, None)
1135111362

1135211363
# 5.13 support for optional accel_uuids argument
1135311364
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)