Skip to content

Commit cffec45

Browse files
committed
compute: Allow retrieval of migration by UUID
The nova API doesn't allow you to retrieve migration records by UUID, only ID. This is confusing. Work around it by listing records and filtering this list. Change-Id: I932c9c70420e85056509513e005bb78168e70611 Signed-off-by: Stephen Finucane <sfinucan@redhat.com>
1 parent 8c975ba commit cffec45

3 files changed

Lines changed: 311 additions & 5 deletions

File tree

openstackclient/compute/v2/server.py

Lines changed: 97 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import json
2222
import logging
2323
import os
24+
import uuid
2425

2526
from cliff import columns as cliff_columns
2627
import iso8601
@@ -3037,6 +3038,18 @@ def take_action(self, parsed_args):
30373038
return self.print_migrations(parsed_args, compute_client, migrations)
30383039

30393040

3041+
def _get_migration_by_uuid(compute_client, server_id, migration_uuid):
3042+
for migration in compute_client.server_migrations.list(server_id):
3043+
if migration.uuid == migration_uuid:
3044+
return migration
3045+
break
3046+
else:
3047+
msg = _(
3048+
'In-progress live migration %s is not found for server %s.'
3049+
)
3050+
raise exceptions.CommandError(msg % (migration_uuid, server_id))
3051+
3052+
30403053
class ShowMigration(command.ShowOne):
30413054
"""Show an in-progress live migration for a given server.
30423055
@@ -3069,13 +3082,38 @@ def take_action(self, parsed_args):
30693082
)
30703083
raise exceptions.CommandError(msg)
30713084

3085+
if not parsed_args.migration.isdigit():
3086+
try:
3087+
uuid.UUID(parsed_args.migration)
3088+
except ValueError:
3089+
msg = _(
3090+
'The <migration> argument must be an ID or UUID'
3091+
)
3092+
raise exceptions.CommandError(msg)
3093+
3094+
if compute_client.api_version < api_versions.APIVersion('2.59'):
3095+
msg = _(
3096+
'--os-compute-api-version 2.59 or greater is required to '
3097+
'retrieve server migrations by UUID'
3098+
)
3099+
raise exceptions.CommandError(msg)
3100+
30723101
server = utils.find_resource(
30733102
compute_client.servers,
30743103
parsed_args.server,
30753104
)
3076-
server_migration = compute_client.server_migrations.get(
3077-
server.id, parsed_args.migration,
3078-
)
3105+
3106+
# the nova API doesn't currently allow retrieval by UUID but it's a
3107+
# reasonably common operation so emulate this behavior by listing
3108+
# migrations - the responses are identical
3109+
if not parsed_args.migration.isdigit():
3110+
server_migration = _get_migration_by_uuid(
3111+
compute_client, server.id, parsed_args.migration,
3112+
)
3113+
else:
3114+
server_migration = compute_client.server_migrations.get(
3115+
server.id, parsed_args.migration,
3116+
)
30793117

30803118
columns = (
30813119
'ID',
@@ -3136,12 +3174,39 @@ def take_action(self, parsed_args):
31363174
)
31373175
raise exceptions.CommandError(msg)
31383176

3177+
if not parsed_args.migration.isdigit():
3178+
try:
3179+
uuid.UUID(parsed_args.migration)
3180+
except ValueError:
3181+
msg = _(
3182+
'The <migration> argument must be an ID or UUID'
3183+
)
3184+
raise exceptions.CommandError(msg)
3185+
3186+
if compute_client.api_version < api_versions.APIVersion('2.59'):
3187+
msg = _(
3188+
'--os-compute-api-version 2.59 or greater is required to '
3189+
'abort server migrations by UUID'
3190+
)
3191+
raise exceptions.CommandError(msg)
3192+
31393193
server = utils.find_resource(
31403194
compute_client.servers,
31413195
parsed_args.server,
31423196
)
3197+
3198+
# the nova API doesn't currently allow retrieval by UUID but it's a
3199+
# reasonably common operation so emulate this behavior by listing
3200+
# migrations - the responses are identical
3201+
migration_id = parsed_args.migration
3202+
if not parsed_args.migration.isdigit():
3203+
migration_id = _get_migration_by_uuid(
3204+
compute_client, server.id, parsed_args.migration,
3205+
).id
3206+
31433207
compute_client.server_migrations.live_migration_abort(
3144-
server.id, parsed_args.migration)
3208+
server.id, migration_id,
3209+
)
31453210

31463211

31473212
class ForceCompleteMigration(command.Command):
@@ -3174,12 +3239,39 @@ def take_action(self, parsed_args):
31743239
)
31753240
raise exceptions.CommandError(msg)
31763241

3242+
if not parsed_args.migration.isdigit():
3243+
try:
3244+
uuid.UUID(parsed_args.migration)
3245+
except ValueError:
3246+
msg = _(
3247+
'The <migration> argument must be an ID or UUID'
3248+
)
3249+
raise exceptions.CommandError(msg)
3250+
3251+
if compute_client.api_version < api_versions.APIVersion('2.59'):
3252+
msg = _(
3253+
'--os-compute-api-version 2.59 or greater is required to '
3254+
'abort server migrations by UUID'
3255+
)
3256+
raise exceptions.CommandError(msg)
3257+
31773258
server = utils.find_resource(
31783259
compute_client.servers,
31793260
parsed_args.server,
31803261
)
3262+
3263+
# the nova API doesn't currently allow retrieval by UUID but it's a
3264+
# reasonably common operation so emulate this behavior by listing
3265+
# migrations - the responses are identical
3266+
migration_id = parsed_args.migration
3267+
if not parsed_args.migration.isdigit():
3268+
migration_id = _get_migration_by_uuid(
3269+
compute_client, server.id, parsed_args.migration,
3270+
).id
3271+
31813272
compute_client.server_migrations.live_migrate_force_complete(
3182-
server.id, parsed_args.migration)
3273+
server.id, migration_id,
3274+
)
31833275

31843276

31853277
class PauseServer(command.Command):

openstackclient/tests/unit/compute/v2/test_server.py

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6232,6 +6232,88 @@ def test_server_migration_show_pre_v224(self):
62326232
'--os-compute-api-version 2.24 or greater is required',
62336233
str(ex))
62346234

6235+
def test_server_migration_show_by_uuid(self):
6236+
self.app.client_manager.compute.api_version = api_versions.APIVersion(
6237+
'2.59')
6238+
self.server_migrations_mock.list.return_value = [self.server_migration]
6239+
6240+
self.columns += ('UUID',)
6241+
self.data += (self.server_migration.uuid,)
6242+
6243+
arglist = [
6244+
self.server.id,
6245+
self.server_migration.uuid, # arbitrary migration UUID
6246+
]
6247+
verifylist = []
6248+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
6249+
6250+
columns, data = self.cmd.take_action(parsed_args)
6251+
6252+
self.assertEqual(self.columns, columns)
6253+
self.assertEqual(self.data, data)
6254+
6255+
self.servers_mock.get.assert_called_with(self.server.id)
6256+
self.server_migrations_mock.list.assert_called_with(self.server.id)
6257+
self.server_migrations_mock.get.assert_not_called()
6258+
6259+
def test_server_migration_show_by_uuid_no_matches(self):
6260+
self.app.client_manager.compute.api_version = api_versions.APIVersion(
6261+
'2.59')
6262+
self.server_migrations_mock.list.return_value = []
6263+
6264+
arglist = [
6265+
self.server.id,
6266+
'69f95745-bfe3-4302-90f7-5b0022cba1ce', # arbitrary migration UUID
6267+
]
6268+
verifylist = []
6269+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
6270+
6271+
ex = self.assertRaises(
6272+
exceptions.CommandError,
6273+
self.cmd.take_action,
6274+
parsed_args)
6275+
self.assertIn(
6276+
'In-progress live migration 69f95745-bfe3-4302-90f7-5b0022cba1ce',
6277+
str(ex))
6278+
6279+
def test_server_migration_show_by_uuid_pre_v259(self):
6280+
self.app.client_manager.compute.api_version = api_versions.APIVersion(
6281+
'2.58')
6282+
6283+
arglist = [
6284+
self.server.id,
6285+
'69f95745-bfe3-4302-90f7-5b0022cba1ce', # arbitrary migration UUID
6286+
]
6287+
verifylist = []
6288+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
6289+
6290+
ex = self.assertRaises(
6291+
exceptions.CommandError,
6292+
self.cmd.take_action,
6293+
parsed_args)
6294+
self.assertIn(
6295+
'--os-compute-api-version 2.59 or greater is required',
6296+
str(ex))
6297+
6298+
def test_server_migration_show_invalid_id(self):
6299+
self.app.client_manager.compute.api_version = api_versions.APIVersion(
6300+
'2.24')
6301+
6302+
arglist = [
6303+
self.server.id,
6304+
'foo', # invalid migration ID
6305+
]
6306+
verifylist = []
6307+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
6308+
6309+
ex = self.assertRaises(
6310+
exceptions.CommandError,
6311+
self.cmd.take_action,
6312+
parsed_args)
6313+
self.assertIn(
6314+
'The <migration> argument must be an ID or UUID',
6315+
str(ex))
6316+
62356317

62366318
class TestServerMigrationAbort(TestServer):
62376319

@@ -6283,6 +6365,69 @@ def test_migration_abort_pre_v224(self):
62836365
'--os-compute-api-version 2.24 or greater is required',
62846366
str(ex))
62856367

6368+
def test_server_migration_abort_by_uuid(self):
6369+
self.app.client_manager.compute.api_version = api_versions.APIVersion(
6370+
'2.59')
6371+
6372+
self.server_migration = compute_fakes.FakeServerMigration\
6373+
.create_one_server_migration()
6374+
self.server_migrations_mock.list.return_value = [self.server_migration]
6375+
6376+
arglist = [
6377+
self.server.id,
6378+
self.server_migration.uuid, # arbitrary migration UUID
6379+
]
6380+
verifylist = []
6381+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
6382+
6383+
result = self.cmd.take_action(parsed_args)
6384+
6385+
self.servers_mock.get.assert_called_with(self.server.id)
6386+
self.server_migrations_mock.list.assert_called_with(self.server.id)
6387+
self.server_migrations_mock.live_migration_abort.assert_called_with(
6388+
self.server.id, self.server_migration.id)
6389+
self.assertIsNone(result)
6390+
6391+
def test_server_migration_abort_by_uuid_no_matches(self):
6392+
self.app.client_manager.compute.api_version = api_versions.APIVersion(
6393+
'2.59')
6394+
6395+
self.server_migrations_mock.list.return_value = []
6396+
6397+
arglist = [
6398+
self.server.id,
6399+
'69f95745-bfe3-4302-90f7-5b0022cba1ce', # arbitrary migration UUID
6400+
]
6401+
verifylist = []
6402+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
6403+
6404+
ex = self.assertRaises(
6405+
exceptions.CommandError,
6406+
self.cmd.take_action,
6407+
parsed_args)
6408+
self.assertIn(
6409+
'In-progress live migration 69f95745-bfe3-4302-90f7-5b0022cba1ce',
6410+
str(ex))
6411+
6412+
def test_server_migration_abort_by_uuid_pre_v259(self):
6413+
self.app.client_manager.compute.api_version = api_versions.APIVersion(
6414+
'2.58')
6415+
6416+
arglist = [
6417+
self.server.id,
6418+
'69f95745-bfe3-4302-90f7-5b0022cba1ce', # arbitrary migration UUID
6419+
]
6420+
verifylist = []
6421+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
6422+
6423+
ex = self.assertRaises(
6424+
exceptions.CommandError,
6425+
self.cmd.take_action,
6426+
parsed_args)
6427+
self.assertIn(
6428+
'--os-compute-api-version 2.59 or greater is required',
6429+
str(ex))
6430+
62866431

62876432
class TestServerMigrationForceComplete(TestServer):
62886433

@@ -6334,6 +6479,69 @@ def test_migration_force_complete_pre_v222(self):
63346479
'--os-compute-api-version 2.22 or greater is required',
63356480
str(ex))
63366481

6482+
def test_server_migration_force_complete_by_uuid(self):
6483+
self.app.client_manager.compute.api_version = api_versions.APIVersion(
6484+
'2.59')
6485+
6486+
self.server_migration = compute_fakes.FakeServerMigration\
6487+
.create_one_server_migration()
6488+
self.server_migrations_mock.list.return_value = [self.server_migration]
6489+
6490+
arglist = [
6491+
self.server.id,
6492+
self.server_migration.uuid, # arbitrary migration UUID
6493+
]
6494+
verifylist = []
6495+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
6496+
6497+
result = self.cmd.take_action(parsed_args)
6498+
6499+
self.servers_mock.get.assert_called_with(self.server.id)
6500+
self.server_migrations_mock.list.assert_called_with(self.server.id)
6501+
self.server_migrations_mock.live_migrate_force_complete\
6502+
.assert_called_with(self.server.id, self.server_migration.id)
6503+
self.assertIsNone(result)
6504+
6505+
def test_server_migration_force_complete_by_uuid_no_matches(self):
6506+
self.app.client_manager.compute.api_version = api_versions.APIVersion(
6507+
'2.59')
6508+
6509+
self.server_migrations_mock.list.return_value = []
6510+
6511+
arglist = [
6512+
self.server.id,
6513+
'69f95745-bfe3-4302-90f7-5b0022cba1ce', # arbitrary migration UUID
6514+
]
6515+
verifylist = []
6516+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
6517+
6518+
ex = self.assertRaises(
6519+
exceptions.CommandError,
6520+
self.cmd.take_action,
6521+
parsed_args)
6522+
self.assertIn(
6523+
'In-progress live migration 69f95745-bfe3-4302-90f7-5b0022cba1ce',
6524+
str(ex))
6525+
6526+
def test_server_migration_force_complete_by_uuid_pre_v259(self):
6527+
self.app.client_manager.compute.api_version = api_versions.APIVersion(
6528+
'2.58')
6529+
6530+
arglist = [
6531+
self.server.id,
6532+
'69f95745-bfe3-4302-90f7-5b0022cba1ce', # arbitrary migration UUID
6533+
]
6534+
verifylist = []
6535+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
6536+
6537+
ex = self.assertRaises(
6538+
exceptions.CommandError,
6539+
self.cmd.take_action,
6540+
parsed_args)
6541+
self.assertIn(
6542+
'--os-compute-api-version 2.59 or greater is required',
6543+
str(ex))
6544+
63376545

63386546
class TestServerPause(TestServer):
63396547

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
features:
3+
- |
4+
The ``server migration abort``, ``server migration force complete`` and
5+
``server migration show`` commands will now accept a server migration UUID
6+
in addition to an ID.

0 commit comments

Comments
 (0)