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
This commit is contained in:
Yikun Jiang 2017-10-30 10:48:58 +08:00 committed by Matt Riedemann
parent 8d80a5e099
commit ad0c036fb6
7 changed files with 206 additions and 17 deletions

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.58") API_MAX_VERSION = api_versions.APIVersion("2.59")

View File

@ -2049,6 +2049,10 @@ class FakeSessionClient(base_client.SessionClient):
migration1.update({"migration_type": "live-migration"}) migration1.update({"migration_type": "live-migration"})
migration2.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 = [] 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']:

View File

@ -28,15 +28,7 @@ class MigrationsTest(utils.TestCase):
for m in ml: for m in ml:
self.assertIsInstance(m, migrations.Migration) self.assertIsInstance(m, migrations.Migration)
self.assertRaises(AttributeError, getattr, m, "migration_type") self.assertRaises(AttributeError, getattr, m, "migration_type")
self.assertRaises(AttributeError, getattr, m, "uuid")
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')
def test_list_migrations_with_filters(self): def test_list_migrations_with_filters(self):
ml = self.cs.migrations.list('host1', 'finished') ml = self.cs.migrations.list('host1', 'finished')
@ -58,3 +50,48 @@ class MigrationsTest(utils.TestCase):
'instance_uuid=instance_id_456&status=finished')) 'instance_uuid=instance_id_456&status=finished'))
self.assertEqual(1, len(ml)) self.assertEqual(1, len(ml))
self.assertEqual('instance_id_456', ml[0].instance_uuid) 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)

View File

@ -3036,14 +3036,55 @@ class ShellTest(utils.TestCase):
self.assert_called('GET', '/os-migrations') self.assert_called('GET', '/os-migrations')
def test_migration_list_v223(self): 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') 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): def test_migration_list_with_filters(self):
self.run_command('migration-list --host host1 --status finished') self.run_command('migration-list --host host1 --status finished')
self.assert_called('GET', self.assert_called('GET',
'/os-migrations?host=host1&status=finished') '/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('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

@ -14,6 +14,7 @@
migration interface migration interface
""" """
from novaclient import api_versions
from novaclient import base from novaclient import base
@ -25,12 +26,8 @@ class Migration(base.Resource):
class MigrationManager(base.ManagerWithFind): class MigrationManager(base.ManagerWithFind):
resource_class = Migration resource_class = Migration
def list(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):
Get a list of migrations.
:param host: (optional) filter migrations by host name.
:param status: (optional) filter migrations by status.
"""
opts = {} opts = {}
if host: if host:
opts['host'] = host opts['host'] = host
@ -38,5 +35,43 @@ class MigrationManager(base.ManagerWithFind):
opts['status'] = status opts['status'] = status
if instance_uuid: if instance_uuid:
opts['instance_uuid'] = 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) 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)

View File

@ -4992,6 +4992,10 @@ def _print_migrations(cs, migrations):
formatters = {'Old Flavor': old_flavor, 'New Flavor': new_flavor} 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"): if cs.api_version >= api_versions.APIVersion("2.23"):
fields.insert(0, "Id") fields.insert(0, "Id")
fields.append("Type") fields.append("Type")
@ -5000,6 +5004,7 @@ def _print_migrations(cs, migrations):
utils.print_list(migrations, fields, formatters) utils.print_list(migrations, fields, formatters)
@api_versions.wraps("2.0", "2.58")
@utils.arg( @utils.arg(
'--instance-uuid', '--instance-uuid',
dest='instance_uuid', dest='instance_uuid',
@ -5020,3 +5025,62 @@ def do_migration_list(cs, args):
migrations = cs.migrations.list(args.host, args.status, migrations = cs.migrations.list(args.host, args.status,
instance_uuid=args.instance_uuid) instance_uuid=args.instance_uuid)
_print_migrations(cs, migrations) _print_migrations(cs, migrations)
@api_versions.wraps("2.59")
@utils.arg(
'--instance-uuid',
dest='instance_uuid',
metavar='<instance_uuid>',
help=_('Fetch migrations for the given instance.'))
@utils.arg(
'--host',
dest='host',
metavar='<host>',
help=_('Fetch migrations for the given host.'))
@utils.arg(
'--status',
dest='status',
metavar='<status>',
help=_('Fetch migrations for the given status.'))
@utils.arg(
'--marker',
dest='marker',
metavar='<marker>',
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='<limit>',
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='<changes_since>',
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)

View File

@ -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.