From ad0c036fb6102a91e66d4004c78f034a21afd8e6 Mon Sep 17 00:00:00 2001 From: Yikun Jiang Date: Mon, 30 Oct 2017 10:48:58 +0800 Subject: [PATCH] Microversion 2.59 - Migrations list pagination Add optional parameters 'limit', 'marker' and 'changes-since' to the os-migrations endpoints for pagination. /os-migrations?limit={limit}&marker={migrations_uuid} /os-migrations?changes-since={changes-since} Change-Id: I7437a61ee07c339b43d008204d1416044a407b68 Implement: blueprint add-pagination-and-change-since-for-migration-list Depends-on: I7e01f95d7173d9217f76e838b3ea71555151ef56 --- novaclient/__init__.py | 2 +- novaclient/tests/unit/v2/fakes.py | 4 ++ novaclient/tests/unit/v2/test_migrations.py | 55 +++++++++++++--- novaclient/tests/unit/v2/test_shell.py | 43 ++++++++++++- novaclient/v2/migrations.py | 47 ++++++++++++-- novaclient/v2/shell.py | 64 +++++++++++++++++++ .../microversion-v2_59-4160c852d7d8812d.yaml | 8 +++ 7 files changed, 206 insertions(+), 17 deletions(-) create mode 100644 releasenotes/notes/microversion-v2_59-4160c852d7d8812d.yaml diff --git a/novaclient/__init__.py b/novaclient/__init__.py index 66520e666..3392aabc9 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.58") +API_MAX_VERSION = api_versions.APIVersion("2.59") diff --git a/novaclient/tests/unit/v2/fakes.py b/novaclient/tests/unit/v2/fakes.py index 7c8f95285..4647d8055 100644 --- a/novaclient/tests/unit/v2/fakes.py +++ b/novaclient/tests/unit/v2/fakes.py @@ -2049,6 +2049,10 @@ class FakeSessionClient(base_client.SessionClient): migration1.update({"migration_type": "live-migration"}) migration2.update({"migration_type": "live-migration"}) + if self.api_version >= api_versions.APIVersion("2.59"): + migration1.update({"uuid": "11111111-07d5-11e1-90e3-e3dffe0c5983"}) + migration2.update({"uuid": "22222222-07d5-11e1-90e3-e3dffe0c5983"}) + migration_list = [] instance_uuid = kw.get('instance_uuid', None) if instance_uuid == migration1['instance_uuid']: diff --git a/novaclient/tests/unit/v2/test_migrations.py b/novaclient/tests/unit/v2/test_migrations.py index 4054012fe..4efa3cb44 100644 --- a/novaclient/tests/unit/v2/test_migrations.py +++ b/novaclient/tests/unit/v2/test_migrations.py @@ -28,15 +28,7 @@ class MigrationsTest(utils.TestCase): for m in ml: self.assertIsInstance(m, migrations.Migration) self.assertRaises(AttributeError, getattr, m, "migration_type") - - def test_list_migrations_v223(self): - cs = fakes.FakeClient(api_versions.APIVersion("2.23")) - ml = cs.migrations.list() - self.assert_request_id(ml, fakes.FAKE_REQUEST_ID_LIST) - cs.assert_called('GET', '/os-migrations') - for m in ml: - self.assertIsInstance(m, migrations.Migration) - self.assertEqual(m.migration_type, 'live-migration') + self.assertRaises(AttributeError, getattr, m, "uuid") def test_list_migrations_with_filters(self): ml = self.cs.migrations.list('host1', 'finished') @@ -58,3 +50,48 @@ class MigrationsTest(utils.TestCase): 'instance_uuid=instance_id_456&status=finished')) self.assertEqual(1, len(ml)) self.assertEqual('instance_id_456', ml[0].instance_uuid) + + +class MigrationsV223Test(MigrationsTest): + def setUp(self): + super(MigrationsV223Test, self).setUp() + self.cs.api_version = api_versions.APIVersion("2.23") + + def test_list_migrations(self): + ml = self.cs.migrations.list() + self.assert_request_id(ml, fakes.FAKE_REQUEST_ID_LIST) + self.cs.assert_called('GET', '/os-migrations') + for m in ml: + self.assertIsInstance(m, migrations.Migration) + self.assertEqual(m.migration_type, 'live-migration') + self.assertRaises(AttributeError, getattr, m, "uuid") + + +class MigrationsV259Test(MigrationsV223Test): + def setUp(self): + super(MigrationsV259Test, self).setUp() + self.cs.api_version = api_versions.APIVersion("2.59") + + def test_list_migrations(self): + ml = self.cs.migrations.list() + self.assert_request_id(ml, fakes.FAKE_REQUEST_ID_LIST) + self.cs.assert_called('GET', '/os-migrations') + for m in ml: + self.assertIsInstance(m, migrations.Migration) + self.assertEqual(m.migration_type, 'live-migration') + self.assertTrue(hasattr(m, 'uuid')) + + def test_list_migrations_with_limit_marker_params(self): + marker = '12140183-c814-4ddf-8453-6df43028aa67' + params = {'limit': 10, + 'marker': marker, + 'changes_since': '2012-02-29T06:23:22'} + + ms = self.cs.migrations.list(**params) + self.assert_request_id(ms, fakes.FAKE_REQUEST_ID_LIST) + self.cs.assert_called('GET', + '/os-migrations?' + 'changes-since=%s&limit=10&marker=%s' + % ('2012-02-29T06%3A23%3A22', marker)) + for m in ms: + self.assertIsInstance(m, migrations.Migration) diff --git a/novaclient/tests/unit/v2/test_shell.py b/novaclient/tests/unit/v2/test_shell.py index 8f416cddd..bba9f7538 100644 --- a/novaclient/tests/unit/v2/test_shell.py +++ b/novaclient/tests/unit/v2/test_shell.py @@ -3036,14 +3036,55 @@ class ShellTest(utils.TestCase): self.assert_called('GET', '/os-migrations') def test_migration_list_v223(self): - self.run_command('migration-list', api_version="2.23") + out, _ = self.run_command('migration-list', api_version="2.23") self.assert_called('GET', '/os-migrations') + # Make sure there is no UUID in the output. Uses "| UUID" to + # avoid collisions with the "Instance UUID" column. + self.assertNotIn('| UUID', out) def test_migration_list_with_filters(self): self.run_command('migration-list --host host1 --status finished') self.assert_called('GET', '/os-migrations?host=host1&status=finished') + def test_migration_list_marker_pre_v259_not_allowed(self): + cmd = 'migration-list --marker %s' + self.assertRaises(SystemExit, self.run_command, + cmd % FAKE_UUID_1, api_version='2.58') + + def test_migration_list_limit_pre_v259_not_allowed(self): + cmd = 'migration-list --limit 10' + self.assertRaises(SystemExit, self.run_command, + cmd, api_version='2.58') + + def test_migration_list_changes_since_pre_v259_not_allowed(self): + cmd = 'migration-list --changes-since 2016-02-29T06:23:22' + self.assertRaises(SystemExit, self.run_command, + cmd, api_version='2.58') + + def test_migration_list_limit_marker_v259(self): + out, _ = self.run_command( + 'migration-list --limit 10 --marker %s' % FAKE_UUID_1, + api_version='2.59') + self.assert_called( + 'GET', + '/os-migrations?limit=10&marker=%s' % FAKE_UUID_1) + # Make sure the UUID column is now in the output. Uses "| UUID" to + # avoid collisions with the "Instance UUID" column. + self.assertIn('| UUID', out) + + def test_migration_list_with_changes_since_v259(self): + self.run_command('migration-list --changes-since 2016-02-29T06:23:22', + api_version='2.59') + self.assert_called( + 'GET', '/os-migrations?changes-since=2016-02-29T06%3A23%3A22') + + def test_migration_list_with_changes_since_invalid_value_v259(self): + ex = self.assertRaises(exceptions.CommandError, self.run_command, + 'migration-list --changes-since 0123456789', + api_version='2.59') + self.assertIn('Invalid changes-since value', six.text_type(ex)) + @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 10e8f37fb..00eb6d110 100644 --- a/novaclient/v2/migrations.py +++ b/novaclient/v2/migrations.py @@ -14,6 +14,7 @@ migration interface """ +from novaclient import api_versions from novaclient import base @@ -25,12 +26,8 @@ class Migration(base.Resource): class MigrationManager(base.ManagerWithFind): resource_class = Migration - def list(self, host=None, status=None, instance_uuid=None): - """ - Get a list of migrations. - :param host: (optional) filter migrations by host name. - :param status: (optional) filter migrations by status. - """ + def _list_base(self, host=None, status=None, instance_uuid=None, + marker=None, limit=None, changes_since=None): opts = {} if host: opts['host'] = host @@ -38,5 +35,43 @@ class MigrationManager(base.ManagerWithFind): opts['status'] = status if instance_uuid: opts['instance_uuid'] = instance_uuid + if marker: + opts['marker'] = marker + if limit: + opts['limit'] = limit + if changes_since: + opts['changes-since'] = changes_since return self._list("/os-migrations", "migrations", filters=opts) + + @api_versions.wraps("2.0", "2.58") + def list(self, host=None, status=None, instance_uuid=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). + """ + return self._list_base(host=host, status=status, + instance_uuid=instance_uuid) + + @api_versions.wraps("2.59") + def list(self, host=None, status=None, instance_uuid=None, + marker=None, limit=None, changes_since=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). + :param changes_since: only return migrations updated after. The + provided time should be an ISO 8061 formatted time. ex + 2016-03-04T06:27:59Z . (optional). + """ + return self._list_base(host=host, status=status, + instance_uuid=instance_uuid, + marker=marker, limit=limit, + changes_since=changes_since) diff --git a/novaclient/v2/shell.py b/novaclient/v2/shell.py index 56828e9fb..4b0079a2e 100644 --- a/novaclient/v2/shell.py +++ b/novaclient/v2/shell.py @@ -4992,6 +4992,10 @@ def _print_migrations(cs, migrations): formatters = {'Old Flavor': old_flavor, 'New Flavor': new_flavor} + # Insert migrations UUID after ID + if cs.api_version >= api_versions.APIVersion("2.59"): + fields.insert(0, "UUID") + if cs.api_version >= api_versions.APIVersion("2.23"): fields.insert(0, "Id") fields.append("Type") @@ -5000,6 +5004,7 @@ def _print_migrations(cs, migrations): utils.print_list(migrations, fields, formatters) +@api_versions.wraps("2.0", "2.58") @utils.arg( '--instance-uuid', dest='instance_uuid', @@ -5020,3 +5025,62 @@ def do_migration_list(cs, args): migrations = cs.migrations.list(args.host, args.status, instance_uuid=args.instance_uuid) _print_migrations(cs, migrations) + + +@api_versions.wraps("2.59") +@utils.arg( + '--instance-uuid', + dest='instance_uuid', + metavar='', + help=_('Fetch migrations for the given instance.')) +@utils.arg( + '--host', + dest='host', + metavar='', + help=_('Fetch migrations for the given host.')) +@utils.arg( + '--status', + dest='status', + metavar='', + help=_('Fetch migrations for the given status.')) +@utils.arg( + '--marker', + dest='marker', + metavar='', + default=None, + help=_('The last migration of the previous page; displays list of ' + 'migrations after "marker". Note that the marker is the ' + 'migration UUID.')) +@utils.arg( + '--limit', + dest='limit', + metavar='', + type=int, + default=None, + help=_('Maximum number of migrations to display. Note that there is a ' + 'configurable max limit on the server, and the limit that is used ' + 'will be the minimum between what is requested here and what ' + 'is configured in the server.')) +@utils.arg( + '--changes-since', + dest='changes_since', + metavar='', + default=None, + help=_('List only migrations changed after a certain point of time. ' + 'The provided time should be an ISO 8061 formatted time. ' + 'ex 2016-03-04T06:27:59Z .')) +def do_migration_list(cs, args): + """Print a list of migrations.""" + if args.changes_since: + try: + timeutils.parse_isotime(args.changes_since) + except ValueError: + raise exceptions.CommandError(_('Invalid changes-since value: %s') + % args.changes_since) + + migrations = cs.migrations.list(args.host, args.status, + instance_uuid=args.instance_uuid, + marker=args.marker, limit=args.limit, + changes_since=args.changes_since) + # TODO(yikun): Output a "Marker" column if there is a next link? + _print_migrations(cs, migrations) diff --git a/releasenotes/notes/microversion-v2_59-4160c852d7d8812d.yaml b/releasenotes/notes/microversion-v2_59-4160c852d7d8812d.yaml new file mode 100644 index 000000000..e90c87b81 --- /dev/null +++ b/releasenotes/notes/microversion-v2_59-4160c852d7d8812d.yaml @@ -0,0 +1,8 @@ +--- +features: + - | + Added support for microversion v2.59 which introduces pagination support + for migrations with the help of new optional parameters ``limit``, + ``marker``, and also adds the new filter ``changes-since``. Users can use + ``changes-since`` filter to filter the results based on the last time the + migration was updated.