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:
parent
8d80a5e099
commit
ad0c036fb6
@ -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")
|
||||||
|
@ -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']:
|
||||||
|
@ -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)
|
||||||
|
@ -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):
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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.
|
Loading…
Reference in New Issue
Block a user