Microversion 2.80: Add user_id/project_id to migration-list API

Add ``user_id`` and ``project_id`` to the ``GET /os-migrations``
API, and it can called ``--user-id <user_id>`` and/or
``--project-id <project_id>`` by ``nova migration-list`` CLI.

Showing the ``user_id`` and ``project_id`` when using api_version>=2.80
with the server-migration-list or server-migration-show APIs.

Depends-On: https://review.opendev.org/#/c/674243/
Part of blueprint add-user-id-field-to-the-migrations-table

Change-Id: I11343ca265ab2b6b6f46877897d8223ef340c258
This commit is contained in:
zhangbailin 2019-08-07 11:36:10 +08:00 committed by Matt Riedemann
parent 6c0e4d7a39
commit 8744bea0e3
8 changed files with 263 additions and 10 deletions

View File

@ -2516,6 +2516,8 @@ nova migration-list
[--limit <limit>] [--limit <limit>]
[--changes-since <changes_since>] [--changes-since <changes_since>]
[--changes-before <changes_before>] [--changes-before <changes_before>]
[--project-id <project_id>]
[--user-id <user_id>]
Print a list of migrations. Print a list of migrations.
@ -2573,6 +2575,14 @@ To see the list of evacuation operations *from* a compute service host:
point of time. The provided time should be an ISO 8061 formatted time. point of time. The provided time should be an ISO 8061 formatted time.
e.g. 2016-03-04T06:27:59Z . (Supported by API versions '2.66' - '2.latest') e.g. 2016-03-04T06:27:59Z . (Supported by API versions '2.66' - '2.latest')
``--project-id <project_id>``
Filter the migrations by the given project ID.
(Supported by API versions '2.80' - '2.latest')
``--user-id <user_id>``
Filter the migrations by the given user ID.
(Supported by API versions '2.80' - '2.latest')
.. _nova_pause: .. _nova_pause:
nova pause nova pause

View File

@ -25,4 +25,4 @@ API_MIN_VERSION = api_versions.APIVersion("2.1")
# when client supported the max version, and bumped sequentially, otherwise # when client supported the max version, and bumped sequentially, otherwise
# the client may break due to server side new version may include some # the client may break due to server side new version may include some
# backward incompatible change. # backward incompatible change.
API_MAX_VERSION = api_versions.APIVersion("2.79") API_MAX_VERSION = api_versions.APIVersion("2.80")

View File

@ -2288,6 +2288,14 @@ class FakeSessionClient(base_client.SessionClient):
migration1.update({"uuid": "11111111-07d5-11e1-90e3-e3dffe0c5983"}) migration1.update({"uuid": "11111111-07d5-11e1-90e3-e3dffe0c5983"})
migration2.update({"uuid": "22222222-07d5-11e1-90e3-e3dffe0c5983"}) migration2.update({"uuid": "22222222-07d5-11e1-90e3-e3dffe0c5983"})
if self.api_version >= api_versions.APIVersion("2.80"):
migration1.update({
"project_id": "b59c18e5fa284fd384987c5cb25a1853",
"user_id": "13cc0930d27c4be0acc14d7c47a3e1f7"})
migration2.update({
"project_id": "b59c18e5fa284fd384987c5cb25a1853",
"user_id": "13cc0930d27c4be0acc14d7c47a3e1f7"})
migration_list = [] migration_list = []
instance_uuid = kw.get('instance_uuid', None) instance_uuid = kw.get('instance_uuid', None)
if instance_uuid == migration1['instance_uuid']: if instance_uuid == migration1['instance_uuid']:
@ -2379,6 +2387,12 @@ class FakeSessionClient(base_client.SessionClient):
"disk_remaining_bytes": 230000, "disk_remaining_bytes": 230000,
"updated_at": "2016-01-29T13:42:02.000000" "updated_at": "2016-01-29T13:42:02.000000"
}} }}
if self.api_version >= api_versions.APIVersion("2.80"):
migration['migration'].update({
"project_id": "b59c18e5fa284fd384987c5cb25a1853",
"user_id": "13cc0930d27c4be0acc14d7c47a3e1f7"})
return (200, FAKE_RESPONSE_HEADERS, migration) return (200, FAKE_RESPONSE_HEADERS, migration)
@api_versions.wraps(start_version="2.23") @api_versions.wraps(start_version="2.23")
@ -2402,6 +2416,12 @@ class FakeSessionClient(base_client.SessionClient):
"disk_remaining_bytes": 230000, "disk_remaining_bytes": 230000,
"updated_at": "2016-01-29T13:42:02.000000" "updated_at": "2016-01-29T13:42:02.000000"
}]} }]}
if self.api_version >= api_versions.APIVersion("2.80"):
migrations['migrations'][0].update({
"project_id": "b59c18e5fa284fd384987c5cb25a1853",
"user_id": "13cc0930d27c4be0acc14d7c47a3e1f7"})
return (200, FAKE_RESPONSE_HEADERS, migrations) return (200, FAKE_RESPONSE_HEADERS, migrations)
def delete_servers_1234_migrations_1(self): def delete_servers_1234_migrations_1(self):

View File

@ -10,6 +10,8 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import six
from novaclient import api_versions from novaclient import api_versions
from novaclient.tests.unit import utils from novaclient.tests.unit import utils
from novaclient.tests.unit.v2 import fakes from novaclient.tests.unit.v2 import fakes
@ -112,3 +114,58 @@ class MigrationsV266Test(MigrationsV259Test):
'2012-02-29T06%3A23%3A22') '2012-02-29T06%3A23%3A22')
for m in ms: for m in ms:
self.assertIsInstance(m, migrations.Migration) self.assertIsInstance(m, migrations.Migration)
class MigrationsV280Test(MigrationsV266Test):
def setUp(self):
super(MigrationsV280Test, self).setUp()
self.cs.api_version = api_versions.APIVersion("2.80")
def test_list_migrations_with_user_id(self):
user_id = '13cc0930d27c4be0acc14d7c47a3e1f7'
params = {'user_id': user_id}
ms = self.cs.migrations.list(**params)
self.assert_request_id(ms, fakes.FAKE_REQUEST_ID_LIST)
self.cs.assert_called('GET', '/os-migrations?user_id=%s' % user_id)
for m in ms:
self.assertIsInstance(m, migrations.Migration)
def test_list_migrations_with_project_id(self):
project_id = 'b59c18e5fa284fd384987c5cb25a1853'
params = {'project_id': project_id}
ms = self.cs.migrations.list(**params)
self.assert_request_id(ms, fakes.FAKE_REQUEST_ID_LIST)
self.cs.assert_called('GET', '/os-migrations?project_id=%s'
% project_id)
for m in ms:
self.assertIsInstance(m, migrations.Migration)
def test_list_migrations_with_user_and_project_id(self):
user_id = '13cc0930d27c4be0acc14d7c47a3e1f7'
project_id = 'b59c18e5fa284fd384987c5cb25a1853'
params = {'user_id': user_id, 'project_id': project_id}
ms = self.cs.migrations.list(**params)
self.assert_request_id(ms, fakes.FAKE_REQUEST_ID_LIST)
self.cs.assert_called('GET',
'/os-migrations?project_id=%s&user_id=%s'
% (project_id, user_id))
for m in ms:
self.assertIsInstance(m, migrations.Migration)
def test_list_migrations_with_user_id_pre_v280(self):
self.cs.api_version = api_versions.APIVersion('2.79')
user_id = '13cc0930d27c4be0acc14d7c47a3e1f7'
ex = self.assertRaises(TypeError,
self.cs.migrations.list,
user_id=user_id)
self.assertIn("unexpected keyword argument 'user_id'",
six.text_type(ex))
def test_list_migrations_with_project_id_pre_v280(self):
self.cs.api_version = api_versions.APIVersion('2.79')
project_id = '23cc0930d27c4be0acc14d7c47a3e1f7'
ex = self.assertRaises(TypeError,
self.cs.migrations.list,
project_id=project_id)
self.assertIn("unexpected keyword argument 'project_id'",
six.text_type(ex))

View File

@ -2955,11 +2955,39 @@ class ShellTest(utils.TestCase):
api_version='2.23') api_version='2.23')
self.assert_called('GET', '/servers/1234/migrations') self.assert_called('GET', '/servers/1234/migrations')
def test_list_migrations_pre_v280(self):
out = self.run_command('server-migration-list sample-server',
api_version='2.79')[0]
self.assert_called('GET', '/servers/1234/migrations')
self.assertNotIn('User ID', out)
self.assertNotIn('Project ID', out)
def test_list_migrations_v280(self):
out = self.run_command('server-migration-list sample-server',
api_version='2.80')[0]
self.assert_called('GET', '/servers/1234/migrations')
self.assertIn('User ID', out)
self.assertIn('Project ID', out)
def test_get_migration(self): def test_get_migration(self):
self.run_command('server-migration-show sample-server 1', self.run_command('server-migration-show sample-server 1',
api_version='2.23') api_version='2.23')
self.assert_called('GET', '/servers/1234/migrations/1') self.assert_called('GET', '/servers/1234/migrations/1')
def test_get_migration_pre_v280(self):
out = self.run_command('server-migration-show sample-server 1',
api_version='2.79')[0]
self.assert_called('GET', '/servers/1234/migrations/1')
self.assertNotIn('user_id', out)
self.assertNotIn('project_id', out)
def test_get_migration_v280(self):
out = self.run_command('server-migration-show sample-server 1',
api_version='2.80')[0]
self.assert_called('GET', '/servers/1234/migrations/1')
self.assertIn('user_id', out)
self.assertIn('project_id', out)
def test_live_migration_abort(self): def test_live_migration_abort(self):
self.run_command('live-migration-abort sample-server 1', self.run_command('live-migration-abort sample-server 1',
api_version='2.24') api_version='2.24')
@ -4063,6 +4091,52 @@ class ShellTest(utils.TestCase):
self.assertRaises(SystemExit, self.run_command, cmd, self.assertRaises(SystemExit, self.run_command, cmd,
api_version='2.65') api_version='2.65')
def test_migration_list_with_user_id_v280(self):
user_id = '13cc0930d27c4be0acc14d7c47a3e1f7'
out = self.run_command('migration-list --user-id %s' % user_id,
api_version='2.80')[0]
self.assert_called('GET', '/os-migrations?user_id=%s' % user_id)
self.assertIn('User ID', out)
self.assertIn('Project ID', out)
def test_migration_list_with_project_id_v280(self):
project_id = 'b59c18e5fa284fd384987c5cb25a1853'
out = self.run_command('migration-list --project-id %s' % project_id,
api_version='2.80')[0]
self.assert_called('GET', '/os-migrations?project_id=%s' % project_id)
self.assertIn('User ID', out)
self.assertIn('Project ID', out)
def test_migration_list_with_user_and_project_id_v280(self):
user_id = '13cc0930d27c4be0acc14d7c47a3e1f7'
project_id = 'b59c18e5fa284fd384987c5cb25a1853'
out = self.run_command('migration-list --project-id %(project_id)s '
'--user-id %(user_id)s' %
{'user_id': user_id, 'project_id': project_id},
api_version='2.80')[0]
self.assert_called('GET', '/os-migrations?project_id=%s&user_id=%s'
% (project_id, user_id))
self.assertIn('User ID', out)
self.assertIn('Project ID', out)
def test_migration_list_with_user_id_pre_v280_not_allowed(self):
user_id = '13cc0930d27c4be0acc14d7c47a3e1f7'
cmd = 'migration-list --user-id %s' % user_id
self.assertRaises(SystemExit, self.run_command, cmd,
api_version='2.79')
def test_migration_list_with_project_id_pre_v280_not_allowed(self):
project_id = 'b59c18e5fa284fd384987c5cb25a1853'
cmd = 'migration-list --project-id %s' % project_id
self.assertRaises(SystemExit, self.run_command, cmd,
api_version='2.79')
def test_migration_list_pre_v280(self):
out = self.run_command('migration-list', api_version='2.79')[0]
self.assert_called('GET', '/os-migrations')
self.assertNotIn('User ID', out)
self.assertNotIn('Project ID', out)
@mock.patch('novaclient.v2.shell._find_server') @mock.patch('novaclient.v2.shell._find_server')
@mock.patch('os.system') @mock.patch('os.system')
def test_ssh(self, mock_system, mock_find_server): def test_ssh(self, mock_system, mock_find_server):

View File

@ -29,7 +29,7 @@ class MigrationManager(base.ManagerWithFind):
def _list_base(self, host=None, status=None, instance_uuid=None, def _list_base(self, host=None, status=None, instance_uuid=None,
marker=None, limit=None, changes_since=None, marker=None, limit=None, changes_since=None,
changes_before=None, migration_type=None, changes_before=None, migration_type=None,
source_compute=None): source_compute=None, user_id=None, project_id=None):
opts = {} opts = {}
if host: if host:
opts['host'] = host opts['host'] = host
@ -49,6 +49,10 @@ class MigrationManager(base.ManagerWithFind):
opts['migration_type'] = migration_type opts['migration_type'] = migration_type
if source_compute: if source_compute:
opts['source_compute'] = source_compute opts['source_compute'] = source_compute
if user_id:
opts['user_id'] = user_id
if project_id:
opts['project_id'] = project_id
return self._list("/os-migrations", "migrations", filters=opts) return self._list("/os-migrations", "migrations", filters=opts)
@ -99,7 +103,7 @@ class MigrationManager(base.ManagerWithFind):
migration_type=migration_type, migration_type=migration_type,
source_compute=source_compute) source_compute=source_compute)
@api_versions.wraps("2.66") @api_versions.wraps("2.66", "2.79")
def list(self, host=None, status=None, instance_uuid=None, def list(self, host=None, status=None, instance_uuid=None,
marker=None, limit=None, changes_since=None, marker=None, limit=None, changes_since=None,
changes_before=None, migration_type=None, source_compute=None): changes_before=None, migration_type=None, source_compute=None):
@ -132,3 +136,42 @@ class MigrationManager(base.ManagerWithFind):
changes_before=changes_before, changes_before=changes_before,
migration_type=migration_type, migration_type=migration_type,
source_compute=source_compute) source_compute=source_compute)
@api_versions.wraps("2.80")
def list(self, host=None, status=None, instance_uuid=None,
marker=None, limit=None, changes_since=None,
changes_before=None, migration_type=None,
source_compute=None, user_id=None, project_id=None):
"""
Get a list of migrations.
:param host: filter migrations by host name (optional).
:param status: filter migrations by status (optional).
:param instance_uuid: filter migrations by instance uuid (optional).
:param marker: Begin returning migrations that appear later in the
migrations list than that represented by this migration UUID
(optional).
:param limit: maximum number of migrations to return (optional).
Note the API server has a configurable default limit. If no limit is
specified here or limit is larger than default, the default limit will
be used.
:param changes_since: Only return migrations changed later or equal
to a certain point of time. The provided time should be an ISO 8061
formatted time. e.g. 2016-03-04T06:27:59Z . (optional).
:param changes_before: Only return migrations changed earlier or
equal to a certain point of time. The provided time should be an ISO
8061 formatted time. e.g. 2016-03-05T06:27:59Z . (optional).
:param migration_type: Filter migrations by type. Valid values are:
evacuation, live-migration, migration, resize
:param source_compute: Filter migrations by source compute host name.
:param user_id: filter migrations by user (optional).
:param project_id: filter migrations by project (optional).
"""
return self._list_base(host=host, status=status,
instance_uuid=instance_uuid,
marker=marker, limit=limit,
changes_since=changes_since,
changes_before=changes_before,
migration_type=migration_type,
source_compute=source_compute,
user_id=user_id,
project_id=project_id)

View File

@ -3597,6 +3597,10 @@ def do_server_migration_list(cs, args):
"memory_remaining_bytes", "disk_total_bytes", "memory_remaining_bytes", "disk_total_bytes",
"disk_processed_bytes", "disk_remaining_bytes"] "disk_processed_bytes", "disk_remaining_bytes"]
if cs.api_version >= api_versions.APIVersion("2.80"):
fields.append("Project ID")
fields.append("User ID")
formatters = map(lambda field: utils.make_field_formatter(field)[1], formatters = map(lambda field: utils.make_field_formatter(field)[1],
format_key) format_key)
formatters = dict(zip(format_name, formatters)) formatters = dict(zip(format_name, formatters))
@ -5391,6 +5395,10 @@ def _print_migrations(cs, migrations):
fields.append("Type") fields.append("Type")
formatters.update({"Type": migration_type}) formatters.update({"Type": migration_type})
if cs.api_version >= api_versions.APIVersion("2.80"):
fields.append("Project ID")
fields.append("User ID")
utils.print_list(migrations, fields, formatters) utils.print_list(migrations, fields, formatters)
@ -5564,6 +5572,20 @@ def do_migration_list(cs, args):
'of time. The provided time should be an ISO 8061 formatted time. ' 'of time. The provided time should be an ISO 8061 formatted time. '
'e.g. 2016-03-04T06:27:59Z .'), 'e.g. 2016-03-04T06:27:59Z .'),
start_version="2.66") start_version="2.66")
@utils.arg(
'--project-id',
dest='project_id',
metavar='<project_id>',
default=None,
help=_('Filter the migrations by the given project ID.'),
start_version='2.80')
@utils.arg(
'--user-id',
dest='user_id',
metavar='<user_id>',
default=None,
help=_('Filter the migrations by the given user ID.'),
start_version='2.80')
def do_migration_list(cs, args): def do_migration_list(cs, args):
"""Print a list of migrations.""" """Print a list of migrations."""
if args.changes_since: if args.changes_since:
@ -5580,13 +5602,20 @@ def do_migration_list(cs, args):
raise exceptions.CommandError(_('Invalid changes-before value: %s') raise exceptions.CommandError(_('Invalid changes-before value: %s')
% args.changes_before) % args.changes_before)
migrations = cs.migrations.list(args.host, args.status, kwargs = dict(
instance_uuid=args.instance_uuid, instance_uuid=args.instance_uuid,
marker=args.marker, limit=args.limit, marker=args.marker,
limit=args.limit,
changes_since=args.changes_since, changes_since=args.changes_since,
changes_before=args.changes_before, changes_before=args.changes_before,
migration_type=args.migration_type, migration_type=args.migration_type,
source_compute=args.source_compute) source_compute=args.source_compute)
if cs.api_version >= api_versions.APIVersion('2.80'):
kwargs['project_id'] = args.project_id
kwargs['user_id'] = args.user_id
migrations = cs.migrations.list(args.host, args.status, **kwargs)
_print_migrations(cs, migrations) _print_migrations(cs, migrations)

View File

@ -0,0 +1,20 @@
---
features:
- |
Added support for `microversion 2.80`_ which adds ``user_id``
and ``project_id`` filter parameters to the ``GET /os-migrations`` API.
New kwargs ``project_id`` and ``user_id`` have been added to
the following python API binding:
- novaclient.v2.migrations.MigrationManager.list
The following CLI changes have been made:
- The ``--project-id`` and ``--user-id`` options are added to the
``nova migration-list`` CLI.
- The ``nova server-migration-list`` and ``nova server-migration-show``
commands will show the ``Project ID`` and ``User ID`` values when
using microversion 2.80 or greater.
.. _microversion 2.80: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id72