From 8744bea0e3ebe5bc4d0d899189bfa0bcdcb0a08f Mon Sep 17 00:00:00 2001 From: zhangbailin Date: Wed, 7 Aug 2019 11:36:10 +0800 Subject: [PATCH] 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 `` and/or ``--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 --- doc/source/cli/nova.rst | 10 +++ novaclient/__init__.py | 2 +- novaclient/tests/unit/v2/fakes.py | 20 +++++ novaclient/tests/unit/v2/test_migrations.py | 57 ++++++++++++++ novaclient/tests/unit/v2/test_shell.py | 74 +++++++++++++++++++ novaclient/v2/migrations.py | 47 +++++++++++- novaclient/v2/shell.py | 43 +++++++++-- .../microversion-v2_80-c2394316f9212865.yaml | 20 +++++ 8 files changed, 263 insertions(+), 10 deletions(-) create mode 100644 releasenotes/notes/microversion-v2_80-c2394316f9212865.yaml diff --git a/doc/source/cli/nova.rst b/doc/source/cli/nova.rst index cada3d62e..b94e52afb 100644 --- a/doc/source/cli/nova.rst +++ b/doc/source/cli/nova.rst @@ -2516,6 +2516,8 @@ nova migration-list [--limit ] [--changes-since ] [--changes-before ] + [--project-id ] + [--user-id ] 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. e.g. 2016-03-04T06:27:59Z . (Supported by API versions '2.66' - '2.latest') +``--project-id `` + Filter the migrations by the given project ID. + (Supported by API versions '2.80' - '2.latest') + +``--user-id `` + Filter the migrations by the given user ID. + (Supported by API versions '2.80' - '2.latest') + .. _nova_pause: nova pause diff --git a/novaclient/__init__.py b/novaclient/__init__.py index a4f9b4eec..c4f56fba0 100644 --- a/novaclient/__init__.py +++ b/novaclient/__init__.py @@ -25,4 +25,4 @@ API_MIN_VERSION = api_versions.APIVersion("2.1") # when client supported the max version, and bumped sequentially, otherwise # the client may break due to server side new version may include some # backward incompatible change. -API_MAX_VERSION = api_versions.APIVersion("2.79") +API_MAX_VERSION = api_versions.APIVersion("2.80") diff --git a/novaclient/tests/unit/v2/fakes.py b/novaclient/tests/unit/v2/fakes.py index 3430679be..6302c116a 100644 --- a/novaclient/tests/unit/v2/fakes.py +++ b/novaclient/tests/unit/v2/fakes.py @@ -2288,6 +2288,14 @@ class FakeSessionClient(base_client.SessionClient): migration1.update({"uuid": "11111111-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 = [] instance_uuid = kw.get('instance_uuid', None) if instance_uuid == migration1['instance_uuid']: @@ -2379,6 +2387,12 @@ class FakeSessionClient(base_client.SessionClient): "disk_remaining_bytes": 230000, "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) @api_versions.wraps(start_version="2.23") @@ -2402,6 +2416,12 @@ class FakeSessionClient(base_client.SessionClient): "disk_remaining_bytes": 230000, "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) def delete_servers_1234_migrations_1(self): diff --git a/novaclient/tests/unit/v2/test_migrations.py b/novaclient/tests/unit/v2/test_migrations.py index fcafa6d29..09d09405c 100644 --- a/novaclient/tests/unit/v2/test_migrations.py +++ b/novaclient/tests/unit/v2/test_migrations.py @@ -10,6 +10,8 @@ # License for the specific language governing permissions and limitations # under the License. +import six + from novaclient import api_versions from novaclient.tests.unit import utils from novaclient.tests.unit.v2 import fakes @@ -112,3 +114,58 @@ class MigrationsV266Test(MigrationsV259Test): '2012-02-29T06%3A23%3A22') for m in ms: 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)) diff --git a/novaclient/tests/unit/v2/test_shell.py b/novaclient/tests/unit/v2/test_shell.py index 06b27ad39..79118afcc 100644 --- a/novaclient/tests/unit/v2/test_shell.py +++ b/novaclient/tests/unit/v2/test_shell.py @@ -2955,11 +2955,39 @@ class ShellTest(utils.TestCase): api_version='2.23') 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): self.run_command('server-migration-show sample-server 1', api_version='2.23') 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): self.run_command('live-migration-abort sample-server 1', api_version='2.24') @@ -4063,6 +4091,52 @@ class ShellTest(utils.TestCase): self.assertRaises(SystemExit, self.run_command, cmd, 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('os.system') def test_ssh(self, mock_system, mock_find_server): diff --git a/novaclient/v2/migrations.py b/novaclient/v2/migrations.py index 3aecd1e7e..1fe764b5e 100644 --- a/novaclient/v2/migrations.py +++ b/novaclient/v2/migrations.py @@ -29,7 +29,7 @@ class MigrationManager(base.ManagerWithFind): def _list_base(self, host=None, status=None, instance_uuid=None, marker=None, limit=None, changes_since=None, changes_before=None, migration_type=None, - source_compute=None): + source_compute=None, user_id=None, project_id=None): opts = {} if host: opts['host'] = host @@ -49,6 +49,10 @@ class MigrationManager(base.ManagerWithFind): opts['migration_type'] = migration_type if 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) @@ -99,7 +103,7 @@ class MigrationManager(base.ManagerWithFind): migration_type=migration_type, 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, marker=None, limit=None, changes_since=None, changes_before=None, migration_type=None, source_compute=None): @@ -132,3 +136,42 @@ class MigrationManager(base.ManagerWithFind): changes_before=changes_before, migration_type=migration_type, 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) diff --git a/novaclient/v2/shell.py b/novaclient/v2/shell.py index b656babaa..2125ea992 100644 --- a/novaclient/v2/shell.py +++ b/novaclient/v2/shell.py @@ -3597,6 +3597,10 @@ def do_server_migration_list(cs, args): "memory_remaining_bytes", "disk_total_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], format_key) formatters = dict(zip(format_name, formatters)) @@ -5391,6 +5395,10 @@ def _print_migrations(cs, migrations): fields.append("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) @@ -5564,6 +5572,20 @@ def do_migration_list(cs, args): 'of time. The provided time should be an ISO 8061 formatted time. ' 'e.g. 2016-03-04T06:27:59Z .'), start_version="2.66") +@utils.arg( + '--project-id', + dest='project_id', + metavar='', + default=None, + help=_('Filter the migrations by the given project ID.'), + start_version='2.80') +@utils.arg( + '--user-id', + dest='user_id', + metavar='', + default=None, + help=_('Filter the migrations by the given user ID.'), + start_version='2.80') def do_migration_list(cs, args): """Print a list of migrations.""" if args.changes_since: @@ -5580,13 +5602,20 @@ def do_migration_list(cs, args): raise exceptions.CommandError(_('Invalid changes-before value: %s') % args.changes_before) - migrations = cs.migrations.list(args.host, args.status, - instance_uuid=args.instance_uuid, - marker=args.marker, limit=args.limit, - changes_since=args.changes_since, - changes_before=args.changes_before, - migration_type=args.migration_type, - source_compute=args.source_compute) + kwargs = dict( + instance_uuid=args.instance_uuid, + marker=args.marker, + limit=args.limit, + changes_since=args.changes_since, + changes_before=args.changes_before, + migration_type=args.migration_type, + 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) diff --git a/releasenotes/notes/microversion-v2_80-c2394316f9212865.yaml b/releasenotes/notes/microversion-v2_80-c2394316f9212865.yaml new file mode 100644 index 000000000..cace47d18 --- /dev/null +++ b/releasenotes/notes/microversion-v2_80-c2394316f9212865.yaml @@ -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