From 92a0fc0b9f89f2472a574ef86c007d209a516a41 Mon Sep 17 00:00:00 2001 From: Kevin_Zheng Date: Thu, 16 Jun 2016 17:28:33 +0800 Subject: [PATCH] Add pagination and Changes-since filter support for os-migrations. This patch adds pagination support and changes-since filter for os-migrations API. Users can now use 'limit' and 'marker' to perform paginate query of running migrations list. Users can also filter the results according to the migrations' updated time. The ``GET /os-migrations`` and server migrations APIs will now return a uuid value in addition to the migrations id in the response, and the query parameter schema of the ``GET /os-migrations`` API no longer allows additional properties. Co-Authored-By: Yikun Jiang Implement: blueprint add-pagination-and-change-since-for-migration-list Change-Id: I7e01f95d7173d9217f76e838b3ea71555151ef56 --- api-ref/source/os-migrations.inc | 22 +++- api-ref/source/parameters.yaml | 54 +++++++++ api-ref/source/server-migrations.inc | 10 +- .../v2.59/migrations-get-with-limit.json | 24 ++++ .../v2.59/migrations-get-with-marker.json | 30 +++++ .../migrations-get-with-timestamp-filter.json | 36 ++++++ .../os-migrations/v2.59/migrations-get.json | 78 +++++++++++++ .../v2.59/migrations-get.json | 21 ++++ .../v2.59/migrations-index.json | 23 ++++ .../versions/v21-version-get-resp.json | 2 +- .../versions/versions-get-resp.json | 2 +- nova/api/openstack/api_version_request.py | 5 +- nova/api/openstack/compute/migrations.py | 85 ++++++++++++-- .../compute/rest_api_version_history.rst | 17 +++ .../openstack/compute/schemas/migrations.py | 18 ++- .../openstack/compute/server_migrations.py | 15 ++- .../api/openstack/compute/views/migrations.py | 24 ++++ .../v2.59/migrations-get-with-limit.json.tpl | 24 ++++ .../v2.59/migrations-get-with-marker.json.tpl | 30 +++++ ...rations-get-with-timestamp-filter.json.tpl | 36 ++++++ .../v2.59/migrations-get.json.tpl | 78 +++++++++++++ .../v2.59/migrations-get.json.tpl | 21 ++++ .../v2.59/migrations-index.json.tpl | 23 ++++ .../api_sample_tests/test_migrations.py | 107 ++++++++++++++++++ .../test_server_migrations.py | 16 +++ .../api/openstack/compute/test_migrations.py | 98 ++++++++++++++++ ...on-for-os-migrations-2f8d5d257b0c5658.yaml | 16 +++ 27 files changed, 883 insertions(+), 32 deletions(-) create mode 100644 doc/api_samples/os-migrations/v2.59/migrations-get-with-limit.json create mode 100644 doc/api_samples/os-migrations/v2.59/migrations-get-with-marker.json create mode 100644 doc/api_samples/os-migrations/v2.59/migrations-get-with-timestamp-filter.json create mode 100644 doc/api_samples/os-migrations/v2.59/migrations-get.json create mode 100644 doc/api_samples/server-migrations/v2.59/migrations-get.json create mode 100644 doc/api_samples/server-migrations/v2.59/migrations-index.json create mode 100644 nova/api/openstack/compute/views/migrations.py create mode 100644 nova/tests/functional/api_sample_tests/api_samples/os-migrations/v2.59/migrations-get-with-limit.json.tpl create mode 100644 nova/tests/functional/api_sample_tests/api_samples/os-migrations/v2.59/migrations-get-with-marker.json.tpl create mode 100644 nova/tests/functional/api_sample_tests/api_samples/os-migrations/v2.59/migrations-get-with-timestamp-filter.json.tpl create mode 100644 nova/tests/functional/api_sample_tests/api_samples/os-migrations/v2.59/migrations-get.json.tpl create mode 100644 nova/tests/functional/api_sample_tests/api_samples/server-migrations/v2.59/migrations-get.json.tpl create mode 100644 nova/tests/functional/api_sample_tests/api_samples/server-migrations/v2.59/migrations-index.json.tpl create mode 100644 releasenotes/notes/bp-add-pagination-for-os-migrations-2f8d5d257b0c5658.yaml diff --git a/api-ref/source/os-migrations.inc b/api-ref/source/os-migrations.inc index 28a2fe4807..098b620a77 100644 --- a/api-ref/source/os-migrations.inc +++ b/api-ref/source/os-migrations.inc @@ -1,14 +1,11 @@ .. -*- rst -*- ========================================= - Migrations (os-migrations) (frozen) + Migrations (os-migrations) ========================================= Shows data on migrations. -.. warning:: The old top-level resource `/os-migrations` is frozen, - it won't be extended anymore. Use /servers/{uuid}/migrations instead. - List Migrations =============== @@ -22,7 +19,7 @@ this operation. Cloud providers can change these permissions through the Normal response codes: 200 -Error response codes: unauthorized(401), forbidden(403) +Error response codes: badRequest(400), unauthorized(401), forbidden(403) Request ------- @@ -35,6 +32,9 @@ Request - migration_type: migration_type - source_compute: migration_source_compute - status: migration_status + - limit: migration_limit + - marker: migration_marker + - changes-since: changes_since_migration Response -------- @@ -55,9 +55,21 @@ Response - updated_at: updated - migration_type: migration_type_2_23 - links: migration_links_2_23 + - uuid: migration_uuid + - migrations_links: migration_next_links_2_59 **Example List Migrations: JSON response** .. literalinclude:: ../../doc/api_samples/os-migrations/migrations-get.json :language: javascript +**Example List Migrations (v2.59):** + +.. literalinclude:: ../../doc/api_samples/os-migrations/v2.59/migrations-get.json + :language: javascript + +**Example List Migrations With Links (v2.59):** + +.. literalinclude:: ../../doc/api_samples/os-migrations/v2.59/migrations-get-with-limit.json + :language: javascript + diff --git a/api-ref/source/parameters.yaml b/api-ref/source/parameters.yaml index a672381ae1..335b15e566 100644 --- a/api-ref/source/parameters.yaml +++ b/api-ref/source/parameters.yaml @@ -446,6 +446,23 @@ changes_since_instance_action: required: false type: string min_version: 2.58 +changes_since_migration: + description: | + Filters the response by a date and time stamp when the migration last + changed. Those changed after the specified date and time stamp are returned. + + The date and time stamp format is `ISO 8601 `_: + :: + + CCYY-MM-DDThh:mm:ss±hh:mm + + The ``±hh:mm`` value, if included, returns the time zone as an offset from UTC. + For example, ``2015-08-27T09:49:58-05:00``. + If you omit the time zone, the UTC time zone is assumed. + in: query + required: false + type: string + min_version: 2.59 changes_since_server: description: | Filters the response by a date and time stamp when the server last @@ -845,6 +862,25 @@ migration_instance_uuid: in: query required: false type: string +migration_limit: + description: | + Requests a page size of items. Returns a number of items up to a limit value. + Use the ``limit`` parameter to make an initial limited request and use the + last-seen item from the response as the ``marker`` parameter value in a + subsequent limited request. + in: query + required: false + type: integer + min_version: 2.59 +migration_marker: + description: | + The UUID of the last-seen migration. Use the ``limit`` parameter to make an + initial limited request and use the last-seen item from the response as + the ``marker`` parameter value in a subsequent limited request. + in: query + required: false + type: string + min_version: 2.59 migration_source_compute: description: | The source compute node of migration to filter. @@ -4084,6 +4120,17 @@ migration_new_flavor_id: in: body required: true type: string +migration_next_links_2_59: + description: | + Links pertaining to the migration. + This parameter is returned when paging and more data is available. + See `API Guide / Links and References + `_ + for more info. + in: body + required: false + type: array + min_version: 2.59 migration_old_flavor_id: description: | The flavor ID of the server when the migration was started. @@ -4098,6 +4145,13 @@ migration_type_2_23: required: true type: string min_version: 2.23 +migration_uuid: + description: | + The UUID of the migration. + in: body + required: true + type: string + min_version: 2.59 migrations: description: | The list of server migration objects. diff --git a/api-ref/source/server-migrations.inc b/api-ref/source/server-migrations.inc index 476bf7687a..e1467983d3 100644 --- a/api-ref/source/server-migrations.inc +++ b/api-ref/source/server-migrations.inc @@ -52,10 +52,11 @@ Response - source_node: migrate_source_node - status: migrate_status - updated_at: updated + - uuid: migration_uuid -**Example List Migrations** +**Example List Migrations (2.59)** -.. literalinclude:: ../../doc/api_samples/server-migrations/v2.23/migrations-index.json +.. literalinclude:: ../../doc/api_samples/server-migrations/v2.59/migrations-index.json :language: javascript Show Migration Details @@ -105,10 +106,11 @@ Response - source_node: migrate_source_node - status: migrate_status - updated_at: updated + - uuid: migration_uuid -**Example Show Migration Details** +**Example Show Migration Details (2.59)** -.. literalinclude:: ../../doc/api_samples/server-migrations/v2.23/migrations-get.json +.. literalinclude:: ../../doc/api_samples/server-migrations/v2.59/migrations-get.json :language: javascript Force Migration Complete Action (force_complete Action) diff --git a/doc/api_samples/os-migrations/v2.59/migrations-get-with-limit.json b/doc/api_samples/os-migrations/v2.59/migrations-get-with-limit.json new file mode 100644 index 0000000000..f281cd98b8 --- /dev/null +++ b/doc/api_samples/os-migrations/v2.59/migrations-get-with-limit.json @@ -0,0 +1,24 @@ +{ + "migrations": [ + { + "created_at": "2016-06-23T14:42:02.000000", + "dest_compute": "compute20", + "dest_host": "5.6.7.8", + "dest_node": "node20", + "id": 4, + "instance_uuid": "9128d044-7b61-403e-b766-7547076ff6c1", + "new_instance_type_id": 6, + "old_instance_type_id": 5, + "source_compute": "compute10", + "source_node": "node10", + "status": "migrating", + "migration_type": "resize", + "updated_at": "2016-06-23T14:42:02.000000", + "uuid": "42341d4b-346a-40d0-83c6-5f4f6892b650" + } + ], + "migrations_links": [{ + "href": "http://openstack.example.com/v2.1/6f70656e737461636b20342065766572/os-migrations?limit=1&marker=42341d4b-346a-40d0-83c6-5f4f6892b650", + "rel": "next" + }] +} diff --git a/doc/api_samples/os-migrations/v2.59/migrations-get-with-marker.json b/doc/api_samples/os-migrations/v2.59/migrations-get-with-marker.json new file mode 100644 index 0000000000..e829087f87 --- /dev/null +++ b/doc/api_samples/os-migrations/v2.59/migrations-get-with-marker.json @@ -0,0 +1,30 @@ +{ + "migrations": [ + { + "created_at": "2016-01-29T11:42:02.000000", + "dest_compute": "compute2", + "dest_host": "1.2.3.4", + "dest_node": "node2", + "id": 1, + "instance_uuid": "8600d31b-d1a1-4632-b2ff-45c2be1a70ff", + "links": [ + { + "href": "http://openstack.example.com/v2.1/6f70656e737461636b20342065766572/servers/8600d31b-d1a1-4632-b2ff-45c2be1a70ff/migrations/1", + "rel": "self" + }, + { + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/8600d31b-d1a1-4632-b2ff-45c2be1a70ff/migrations/1", + "rel": "bookmark" + } + ], + "new_instance_type_id": 1, + "old_instance_type_id": 1, + "source_compute": "compute1", + "source_node": "node1", + "status": "running", + "migration_type": "live-migration", + "updated_at": "2016-01-29T11:42:02.000000", + "uuid": "12341d4b-346a-40d0-83c6-5f4f6892b650" + } + ] +} diff --git a/doc/api_samples/os-migrations/v2.59/migrations-get-with-timestamp-filter.json b/doc/api_samples/os-migrations/v2.59/migrations-get-with-timestamp-filter.json new file mode 100644 index 0000000000..7d36fe4548 --- /dev/null +++ b/doc/api_samples/os-migrations/v2.59/migrations-get-with-timestamp-filter.json @@ -0,0 +1,36 @@ +{ + "migrations": [ + { + "created_at": "2016-06-23T14:42:02.000000", + "dest_compute": "compute20", + "dest_host": "5.6.7.8", + "dest_node": "node20", + "id": 4, + "instance_uuid": "9128d044-7b61-403e-b766-7547076ff6c1", + "new_instance_type_id": 6, + "old_instance_type_id": 5, + "source_compute": "compute10", + "source_node": "node10", + "status": "migrating", + "migration_type": "resize", + "updated_at": "2016-06-23T14:42:02.000000", + "uuid": "42341d4b-346a-40d0-83c6-5f4f6892b650" + }, + { + "created_at": "2016-06-23T13:42:02.000000", + "dest_compute": "compute20", + "dest_host": "5.6.7.8", + "dest_node": "node20", + "id": 3, + "instance_uuid": "9128d044-7b61-403e-b766-7547076ff6c1", + "new_instance_type_id": 6, + "old_instance_type_id": 5, + "source_compute": "compute10", + "source_node": "node10", + "status": "error", + "migration_type": "resize", + "updated_at": "2016-06-23T13:42:02.000000", + "uuid": "32341d4b-346a-40d0-83c6-5f4f6892b650" + } + ] +} diff --git a/doc/api_samples/os-migrations/v2.59/migrations-get.json b/doc/api_samples/os-migrations/v2.59/migrations-get.json new file mode 100644 index 0000000000..42ffca8963 --- /dev/null +++ b/doc/api_samples/os-migrations/v2.59/migrations-get.json @@ -0,0 +1,78 @@ +{ + "migrations": [ + { + "created_at": "2016-06-23T14:42:02.000000", + "dest_compute": "compute20", + "dest_host": "5.6.7.8", + "dest_node": "node20", + "id": 4, + "instance_uuid": "9128d044-7b61-403e-b766-7547076ff6c1", + "new_instance_type_id": 6, + "old_instance_type_id": 5, + "source_compute": "compute10", + "source_node": "node10", + "status": "migrating", + "migration_type": "resize", + "updated_at": "2016-06-23T14:42:02.000000", + "uuid": "42341d4b-346a-40d0-83c6-5f4f6892b650" + }, + { + "created_at": "2016-06-23T13:42:02.000000", + "dest_compute": "compute20", + "dest_host": "5.6.7.8", + "dest_node": "node20", + "id": 3, + "instance_uuid": "9128d044-7b61-403e-b766-7547076ff6c1", + "new_instance_type_id": 6, + "old_instance_type_id": 5, + "source_compute": "compute10", + "source_node": "node10", + "status": "error", + "migration_type": "resize", + "updated_at": "2016-06-23T13:42:02.000000", + "uuid": "32341d4b-346a-40d0-83c6-5f4f6892b650" + }, + { + "created_at": "2016-01-29T12:42:02.000000", + "dest_compute": "compute2", + "dest_host": "1.2.3.4", + "dest_node": "node2", + "id": 2, + "instance_uuid": "8600d31b-d1a1-4632-b2ff-45c2be1a70ff", + "new_instance_type_id": 1, + "old_instance_type_id": 1, + "source_compute": "compute1", + "source_node": "node1", + "status": "error", + "migration_type": "live-migration", + "updated_at": "2016-01-29T12:42:02.000000", + "uuid": "22341d4b-346a-40d0-83c6-5f4f6892b650" + }, + { + "created_at": "2016-01-29T11:42:02.000000", + "dest_compute": "compute2", + "dest_host": "1.2.3.4", + "dest_node": "node2", + "id": 1, + "instance_uuid": "8600d31b-d1a1-4632-b2ff-45c2be1a70ff", + "links": [ + { + "href": "http://openstack.example.com/v2.1/6f70656e737461636b20342065766572/servers/8600d31b-d1a1-4632-b2ff-45c2be1a70ff/migrations/1", + "rel": "self" + }, + { + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/8600d31b-d1a1-4632-b2ff-45c2be1a70ff/migrations/1", + "rel": "bookmark" + } + ], + "new_instance_type_id": 1, + "old_instance_type_id": 1, + "source_compute": "compute1", + "source_node": "node1", + "status": "running", + "migration_type": "live-migration", + "updated_at": "2016-01-29T11:42:02.000000", + "uuid": "12341d4b-346a-40d0-83c6-5f4f6892b650" + } + ] +} diff --git a/doc/api_samples/server-migrations/v2.59/migrations-get.json b/doc/api_samples/server-migrations/v2.59/migrations-get.json new file mode 100644 index 0000000000..213b669846 --- /dev/null +++ b/doc/api_samples/server-migrations/v2.59/migrations-get.json @@ -0,0 +1,21 @@ +{ + "migration": { + "created_at": "2016-01-29T13:42:02.000000", + "dest_compute": "compute2", + "dest_host": "1.2.3.4", + "dest_node": "node2", + "id": 1, + "server_uuid": "4cfba335-03d8-49b2-8c52-e69043d1e8fe", + "source_compute": "compute1", + "source_node": "node1", + "status": "running", + "memory_total_bytes": 123456, + "memory_processed_bytes": 12345, + "memory_remaining_bytes": 111111, + "disk_total_bytes": 234567, + "disk_processed_bytes": 23456, + "disk_remaining_bytes": 211111, + "updated_at": "2016-01-29T13:42:02.000000", + "uuid": "12341d4b-346a-40d0-83c6-5f4f6892b650" + } +} diff --git a/doc/api_samples/server-migrations/v2.59/migrations-index.json b/doc/api_samples/server-migrations/v2.59/migrations-index.json new file mode 100644 index 0000000000..6c65c98de4 --- /dev/null +++ b/doc/api_samples/server-migrations/v2.59/migrations-index.json @@ -0,0 +1,23 @@ +{ + "migrations": [ + { + "created_at": "2016-01-29T13:42:02.000000", + "dest_compute": "compute2", + "dest_host": "1.2.3.4", + "dest_node": "node2", + "id": 1, + "server_uuid": "4cfba335-03d8-49b2-8c52-e69043d1e8fe", + "source_compute": "compute1", + "source_node": "node1", + "status": "running", + "memory_total_bytes": 123456, + "memory_processed_bytes": 12345, + "memory_remaining_bytes": 111111, + "disk_total_bytes": 234567, + "disk_processed_bytes": 23456, + "disk_remaining_bytes": 211111, + "updated_at": "2016-01-29T13:42:02.000000", + "uuid": "12341d4b-346a-40d0-83c6-5f4f6892b650" + } + ] +} diff --git a/doc/api_samples/versions/v21-version-get-resp.json b/doc/api_samples/versions/v21-version-get-resp.json index 141bce3d7e..fc6a6d37cd 100644 --- a/doc/api_samples/versions/v21-version-get-resp.json +++ b/doc/api_samples/versions/v21-version-get-resp.json @@ -19,7 +19,7 @@ } ], "status": "CURRENT", - "version": "2.58", + "version": "2.59", "min_version": "2.1", "updated": "2013-07-23T11:33:21Z" } diff --git a/doc/api_samples/versions/versions-get-resp.json b/doc/api_samples/versions/versions-get-resp.json index dafc1d0c00..00a140d912 100644 --- a/doc/api_samples/versions/versions-get-resp.json +++ b/doc/api_samples/versions/versions-get-resp.json @@ -22,7 +22,7 @@ } ], "status": "CURRENT", - "version": "2.58", + "version": "2.59", "min_version": "2.1", "updated": "2013-07-23T11:33:21Z" } diff --git a/nova/api/openstack/api_version_request.py b/nova/api/openstack/api_version_request.py index 85d9de756f..9a629f6458 100644 --- a/nova/api/openstack/api_version_request.py +++ b/nova/api/openstack/api_version_request.py @@ -139,6 +139,9 @@ REST_API_VERSION_HISTORY = """REST API Version History: related limits and quota resources are also removed. * 2.58 - Add pagination support and changes-since filter for os-instance-actions API. + * 2.59 - Add pagination support and changes-since filter for os-migrations + API. And the os-migrations API now returns both the id and the + uuid in response. """ # The minimum and maximum versions of the API supported @@ -147,7 +150,7 @@ REST_API_VERSION_HISTORY = """REST API Version History: # Note(cyeoh): This only applies for the v2.1 API once microversions # support is fully merged. It does not affect the V2 API. _MIN_API_VERSION = "2.1" -_MAX_API_VERSION = "2.58" +_MAX_API_VERSION = "2.59" DEFAULT_API_VERSION = _MIN_API_VERSION # Almost all proxy APIs which are related to network, images and baremetal diff --git a/nova/api/openstack/compute/migrations.py b/nova/api/openstack/compute/migrations.py index 7190a1f02d..d033e60c61 100644 --- a/nova/api/openstack/compute/migrations.py +++ b/nova/api/openstack/compute/migrations.py @@ -10,12 +10,16 @@ # License for the specific language governing permissions and limitations # under the License. -from nova.api.openstack import api_version_request +from oslo_utils import timeutils +from webob import exc + from nova.api.openstack import common from nova.api.openstack.compute.schemas import migrations as schema_migrations +from nova.api.openstack.compute.views import migrations as migrations_view from nova.api.openstack import wsgi from nova.api import validation from nova import compute +from nova import exception from nova.objects import base as obj_base from nova.policies import migrations as migrations_policies @@ -23,14 +27,14 @@ from nova.policies import migrations as migrations_policies class MigrationsController(wsgi.Controller): """Controller for accessing migrations in OpenStack API.""" - _view_builder_class = common.ViewBuilder + _view_builder_class = migrations_view.ViewBuilder _collection_name = "servers/%s/migrations" def __init__(self): super(MigrationsController, self).__init__() self.compute_api = compute.API() - def _output(self, req, migrations_obj, add_link=False): + def _output(self, req, migrations_obj, add_link=False, add_uuid=False): """Returns the desired output of the API from an object. From a MigrationsList's object this method returns a list of @@ -51,7 +55,8 @@ class MigrationsController(wsgi.Controller): del obj['deleted'] del obj['deleted_at'] del obj['hidden'] - del obj['uuid'] + if not add_uuid: + del obj['uuid'] if 'memory_total' in obj: for key in detail_keys: del obj[key] @@ -68,15 +73,71 @@ class MigrationsController(wsgi.Controller): return objects - @wsgi.expected_errors(()) - @validation.query_schema(schema_migrations.list_query_schema_v20) - def index(self, req): - """Return all migrations using the query parameters as filters.""" + def _index(self, req, add_link=False, next_link=False, add_uuid=False, + sort_dirs=None, sort_keys=None, limit=None, marker=None, + allow_changes_since=False): context = req.environ['nova.context'] context.can(migrations_policies.POLICY_ROOT % 'index') - migrations = self.compute_api.get_migrations(context, req.GET) + search_opts = {} + search_opts.update(req.GET) + if 'changes-since' in search_opts: + if allow_changes_since: + search_opts['changes-since'] = timeutils.parse_isotime( + search_opts['changes-since']) + else: + # Before microversion 2.59, the changes-since filter was not + # supported in the DB API. However, the schema allowed + # additionalProperties=True, so a user could pass it before + # 2.59 and filter by the updated_at field if we don't remove + # it from search_opts. + del search_opts['changes-since'] - if api_version_request.is_supported(req, min_version='2.23'): - return {'migrations': self._output(req, migrations, True)} + if sort_keys: + try: + migrations = self.compute_api.get_migrations_sorted( + context, search_opts, + sort_dirs=sort_dirs, sort_keys=sort_keys, + limit=limit, marker=marker) + except exception.MarkerNotFound as e: + raise exc.HTTPBadRequest(explanation=e.format_message()) + else: + migrations = self.compute_api.get_migrations( + context, search_opts) - return {'migrations': self._output(req, migrations)} + migrations = self._output(req, migrations, add_link, add_uuid) + migrations_dict = {'migrations': migrations} + + if next_link: + migrations_links = self._view_builder.get_links(req, migrations) + if migrations_links: + migrations_dict['migrations_links'] = migrations_links + return migrations_dict + + @wsgi.Controller.api_version("2.1", "2.22") # noqa + @wsgi.expected_errors(()) + @validation.query_schema(schema_migrations.list_query_schema_v20, + "2.1", "2.22") + def index(self, req): + """Return all migrations using the query parameters as filters.""" + return self._index(req) + + @wsgi.Controller.api_version("2.23", "2.58") # noqa + @wsgi.expected_errors(()) + @validation.query_schema(schema_migrations.list_query_schema_v20, + "2.23", "2.58") + def index(self, req): + """Return all migrations using the query parameters as filters.""" + return self._index(req, add_link=True) + + @wsgi.Controller.api_version("2.59") # noqa + @wsgi.expected_errors(400) + @validation.query_schema(schema_migrations.list_query_params_v259, + "2.59") + def index(self, req): + """Return all migrations using the query parameters as filters.""" + limit, marker = common.get_limit_and_marker(req) + return self._index(req, add_link=True, next_link=True, add_uuid=True, + sort_keys=['created_at', 'id'], + sort_dirs=['desc', 'desc'], + limit=limit, marker=marker, + allow_changes_since=True) diff --git a/nova/api/openstack/compute/rest_api_version_history.rst b/nova/api/openstack/compute/rest_api_version_history.rst index ae19a50afa..7aff737c29 100644 --- a/nova/api/openstack/compute/rest_api_version_history.rst +++ b/nova/api/openstack/compute/rest_api_version_history.rst @@ -740,3 +740,20 @@ API. Users can now use ``limit`` and ``marker`` to perform paginated query when listing instance actions. Users can also use ``changes-since`` filter to filter the results based on the last time the instance action was updated. + +2.59 +---- + +Added pagination support for migrations, there are four changes: + +* Add pagination support and ``changes-since`` filter for os-migrations + API. Users can now use ``limit`` and ``marker`` to perform paginate query + when listing migrations. +* Users can also use ``changes-since`` filter to filter the results based + on the last time the migration record was updated. +* ``GET /os-migrations``, + ``GET /servers/{server_id}/migrations/{migration_id}`` and + ``GET /servers/{server_id}/migrations`` will now return a uuid value in + addition to the migrations id in the response. +* The query parameter schema of the ``GET /os-migrations`` API no longer + allows additional properties. diff --git a/nova/api/openstack/compute/schemas/migrations.py b/nova/api/openstack/compute/schemas/migrations.py index 49c70efc00..31f07b0ce3 100644 --- a/nova/api/openstack/compute/schemas/migrations.py +++ b/nova/api/openstack/compute/schemas/migrations.py @@ -12,6 +12,8 @@ # License for the specific language governing permissions and limitations # under the License. +import copy + from nova.api.validation import parameter_types list_query_schema_v20 = { @@ -25,9 +27,17 @@ list_query_schema_v20 = { 'migration_type': parameter_types.common_query_param, }, # For backward compatible changes - # TODO(mriedem): In a future microversion, consider changing - # additionalProperties to False, remove the 'hidden' filter since - # it's vestigial, and enforce type and enum checks for the filters - # where that makes sense, e.g. instance_uuid, status and migration_type. 'additionalProperties': True } + +list_query_params_v259 = copy.deepcopy(list_query_schema_v20) +list_query_params_v259['properties'].update({ + # The 2.59 microversion added support for paging by limit and marker + # and filtering by changes-since. + 'limit': parameter_types.single_param( + parameter_types.non_negative_integer), + 'marker': parameter_types.single_param({'type': 'string'}), + 'changes-since': parameter_types.single_param( + {'type': 'string', 'format': 'date-time'}), +}) +list_query_params_v259['additionalProperties'] = False diff --git a/nova/api/openstack/compute/server_migrations.py b/nova/api/openstack/compute/server_migrations.py index 3a27ed8d52..91e5ed58b8 100644 --- a/nova/api/openstack/compute/server_migrations.py +++ b/nova/api/openstack/compute/server_migrations.py @@ -15,6 +15,7 @@ from webob import exc +from nova.api.openstack import api_version_request from nova.api.openstack import common from nova.api.openstack.compute.schemas import server_migrations from nova.api.openstack import wsgi @@ -25,13 +26,13 @@ from nova.i18n import _ from nova.policies import servers_migrations as sm_policies -def output(migration): +def output(migration, include_uuid=False): """Returns the desired output of the API from an object. From a Migrations's object this method returns the primitive object with the only necessary and expected fields. """ - return { + result = { "created_at": migration.created_at, "dest_compute": migration.dest_compute, "dest_host": migration.dest_host, @@ -49,6 +50,9 @@ def output(migration): "status": migration.status, "updated_at": migration.updated_at } + if include_uuid: + result['uuid'] = migration.uuid + return result class ServerMigrationsController(wsgi.Controller): @@ -96,7 +100,9 @@ class ServerMigrationsController(wsgi.Controller): migrations = self.compute_api.get_migrations_in_progress_by_instance( context, server_id, 'live-migration') - return {'migrations': [output(migration) for migration in migrations]} + include_uuid = api_version_request.is_supported(req, '2.59') + return {'migrations': [output( + migration, include_uuid) for migration in migrations]} @wsgi.Controller.api_version("2.23") @wsgi.expected_errors(404) @@ -129,7 +135,8 @@ class ServerMigrationsController(wsgi.Controller): " progress.") % {"id": id, "uuid": server_id} raise exc.HTTPNotFound(explanation=msg) - return {'migration': output(migration)} + include_uuid = api_version_request.is_supported(req, '2.59') + return {'migration': output(migration, include_uuid)} @wsgi.Controller.api_version("2.24") @wsgi.response(202) diff --git a/nova/api/openstack/compute/views/migrations.py b/nova/api/openstack/compute/views/migrations.py new file mode 100644 index 0000000000..cfcb103cea --- /dev/null +++ b/nova/api/openstack/compute/views/migrations.py @@ -0,0 +1,24 @@ +# Copyright 2017 Huawei Technologies Co.,LTD. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from nova.api.openstack import common + + +class ViewBuilder(common.ViewBuilder): + + _collection_name = "os-migrations" + + def get_links(self, request, migrations): + return self._get_collection_links(request, migrations, + self._collection_name, 'uuid') diff --git a/nova/tests/functional/api_sample_tests/api_samples/os-migrations/v2.59/migrations-get-with-limit.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/os-migrations/v2.59/migrations-get-with-limit.json.tpl new file mode 100644 index 0000000000..e98614278f --- /dev/null +++ b/nova/tests/functional/api_sample_tests/api_samples/os-migrations/v2.59/migrations-get-with-limit.json.tpl @@ -0,0 +1,24 @@ +{ + "migrations": [ + { + "created_at": "2016-06-23T14:42:02.000000", + "dest_compute": "compute20", + "dest_host": "5.6.7.8", + "dest_node": "node20", + "id": 4, + "instance_uuid": "%(instance_2)s", + "new_instance_type_id": 6, + "old_instance_type_id": 5, + "source_compute": "compute10", + "source_node": "node10", + "status": "migrating", + "migration_type": "resize", + "updated_at": "2016-06-23T14:42:02.000000", + "uuid": "42341d4b-346a-40d0-83c6-5f4f6892b650" + } + ], + "migrations_links": [{ + "href": "%(host)s/v2.1/6f70656e737461636b20342065766572/os-migrations?limit=1&marker=42341d4b-346a-40d0-83c6-5f4f6892b650", + "rel": "next" + }] +} diff --git a/nova/tests/functional/api_sample_tests/api_samples/os-migrations/v2.59/migrations-get-with-marker.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/os-migrations/v2.59/migrations-get-with-marker.json.tpl new file mode 100644 index 0000000000..ff0ecd91d5 --- /dev/null +++ b/nova/tests/functional/api_sample_tests/api_samples/os-migrations/v2.59/migrations-get-with-marker.json.tpl @@ -0,0 +1,30 @@ +{ + "migrations": [ + { + "created_at": "2016-01-29T11:42:02.000000", + "dest_compute": "compute2", + "dest_host": "1.2.3.4", + "dest_node": "node2", + "id": 1, + "instance_uuid": "%(instance_1)s", + "links": [ + { + "href": "%(host)s/v2.1/6f70656e737461636b20342065766572/servers/%(instance_1)s/migrations/1", + "rel": "self" + }, + { + "href": "%(host)s/6f70656e737461636b20342065766572/servers/%(instance_1)s/migrations/1", + "rel": "bookmark" + } + ], + "new_instance_type_id": 1, + "old_instance_type_id": 1, + "source_compute": "compute1", + "source_node": "node1", + "migration_type": "live-migration", + "status": "running", + "updated_at": "2016-01-29T11:42:02.000000", + "uuid": "12341d4b-346a-40d0-83c6-5f4f6892b650" + } + ] +} diff --git a/nova/tests/functional/api_sample_tests/api_samples/os-migrations/v2.59/migrations-get-with-timestamp-filter.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/os-migrations/v2.59/migrations-get-with-timestamp-filter.json.tpl new file mode 100644 index 0000000000..f2e7ba5b26 --- /dev/null +++ b/nova/tests/functional/api_sample_tests/api_samples/os-migrations/v2.59/migrations-get-with-timestamp-filter.json.tpl @@ -0,0 +1,36 @@ +{ + "migrations": [ + { + "created_at": "2016-06-23T14:42:02.000000", + "dest_compute": "compute20", + "dest_host": "5.6.7.8", + "dest_node": "node20", + "id": 4, + "instance_uuid": "%(instance_2)s", + "new_instance_type_id": 6, + "old_instance_type_id": 5, + "source_compute": "compute10", + "source_node": "node10", + "migration_type": "resize", + "status": "migrating", + "updated_at": "2016-06-23T14:42:02.000000", + "uuid": "42341d4b-346a-40d0-83c6-5f4f6892b650" + }, + { + "created_at": "2016-06-23T13:42:02.000000", + "dest_compute": "compute20", + "dest_host": "5.6.7.8", + "dest_node": "node20", + "id": 3, + "instance_uuid": "%(instance_2)s", + "new_instance_type_id": 6, + "old_instance_type_id": 5, + "source_compute": "compute10", + "source_node": "node10", + "migration_type": "resize", + "status": "error", + "updated_at": "2016-06-23T13:42:02.000000", + "uuid": "32341d4b-346a-40d0-83c6-5f4f6892b650" + } + ] +} diff --git a/nova/tests/functional/api_sample_tests/api_samples/os-migrations/v2.59/migrations-get.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/os-migrations/v2.59/migrations-get.json.tpl new file mode 100644 index 0000000000..78033a6155 --- /dev/null +++ b/nova/tests/functional/api_sample_tests/api_samples/os-migrations/v2.59/migrations-get.json.tpl @@ -0,0 +1,78 @@ +{ + "migrations": [ + { + "created_at": "2016-06-23T14:42:02.000000", + "dest_compute": "compute20", + "dest_host": "5.6.7.8", + "dest_node": "node20", + "id": 4, + "instance_uuid": "%(instance_2)s", + "new_instance_type_id": 6, + "old_instance_type_id": 5, + "source_compute": "compute10", + "source_node": "node10", + "migration_type": "resize", + "status": "migrating", + "updated_at": "2016-06-23T14:42:02.000000", + "uuid": "42341d4b-346a-40d0-83c6-5f4f6892b650" + }, + { + "created_at": "2016-06-23T13:42:02.000000", + "dest_compute": "compute20", + "dest_host": "5.6.7.8", + "dest_node": "node20", + "id": 3, + "instance_uuid": "%(instance_2)s", + "new_instance_type_id": 6, + "old_instance_type_id": 5, + "source_compute": "compute10", + "source_node": "node10", + "migration_type": "resize", + "status": "error", + "updated_at": "2016-06-23T13:42:02.000000", + "uuid": "32341d4b-346a-40d0-83c6-5f4f6892b650" + }, + { + "created_at": "2016-01-29T12:42:02.000000", + "dest_compute": "compute2", + "dest_host": "1.2.3.4", + "dest_node": "node2", + "id": 2, + "instance_uuid": "%(instance_1)s", + "new_instance_type_id": 1, + "old_instance_type_id": 1, + "source_compute": "compute1", + "source_node": "node1", + "migration_type": "live-migration", + "status": "error", + "updated_at": "2016-01-29T12:42:02.000000", + "uuid": "22341d4b-346a-40d0-83c6-5f4f6892b650" + }, + { + "created_at": "2016-01-29T11:42:02.000000", + "dest_compute": "compute2", + "dest_host": "1.2.3.4", + "dest_node": "node2", + "id": 1, + "instance_uuid": "%(instance_1)s", + "links": [ + { + "href": "%(host)s/v2.1/6f70656e737461636b20342065766572/servers/%(instance_1)s/migrations/1", + "rel": "self" + }, + { + "href": "%(host)s/6f70656e737461636b20342065766572/servers/%(instance_1)s/migrations/1", + "rel": "bookmark" + } + ], + "new_instance_type_id": 1, + "old_instance_type_id": 1, + "source_compute": "compute1", + "source_node": "node1", + "migration_type": "live-migration", + "status": "running", + "updated_at": "2016-01-29T11:42:02.000000", + "uuid": "12341d4b-346a-40d0-83c6-5f4f6892b650" + } + ] +} diff --git a/nova/tests/functional/api_sample_tests/api_samples/server-migrations/v2.59/migrations-get.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/server-migrations/v2.59/migrations-get.json.tpl new file mode 100644 index 0000000000..b2681666f5 --- /dev/null +++ b/nova/tests/functional/api_sample_tests/api_samples/server-migrations/v2.59/migrations-get.json.tpl @@ -0,0 +1,21 @@ +{ + "migration": { + "created_at": "2016-01-29T13:42:02.000000", + "dest_compute": "compute2", + "dest_host": "1.2.3.4", + "dest_node": "node2", + "id": 1, + "server_uuid": "%(server_uuid)s", + "source_compute": "compute1", + "source_node": "node1", + "status": "running", + "memory_total_bytes": 123456, + "memory_processed_bytes": 12345, + "memory_remaining_bytes": 111111, + "disk_total_bytes": 234567, + "disk_processed_bytes": 23456, + "disk_remaining_bytes": 211111, + "updated_at": "2016-01-29T13:42:02.000000", + "uuid": "12341d4b-346a-40d0-83c6-5f4f6892b650" + } +} diff --git a/nova/tests/functional/api_sample_tests/api_samples/server-migrations/v2.59/migrations-index.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/server-migrations/v2.59/migrations-index.json.tpl new file mode 100644 index 0000000000..c99d5989ea --- /dev/null +++ b/nova/tests/functional/api_sample_tests/api_samples/server-migrations/v2.59/migrations-index.json.tpl @@ -0,0 +1,23 @@ +{ + "migrations": [ + { + "created_at": "2016-01-29T13:42:02.000000", + "dest_compute": "compute2", + "dest_host": "1.2.3.4", + "dest_node": "node2", + "id": 1, + "server_uuid": "%(server_uuid_1)s", + "source_compute": "compute1", + "source_node": "node1", + "status": "running", + "memory_total_bytes": 123456, + "memory_processed_bytes": 12345, + "memory_remaining_bytes": 111111, + "disk_total_bytes": 234567, + "disk_processed_bytes": 23456, + "disk_remaining_bytes": 211111, + "updated_at": "2016-01-29T13:42:02.000000", + "uuid": "12341d4b-346a-40d0-83c6-5f4f6892b650" + } + ] +} diff --git a/nova/tests/functional/api_sample_tests/test_migrations.py b/nova/tests/functional/api_sample_tests/test_migrations.py index c3dcc24f78..8b888f5dd9 100644 --- a/nova/tests/functional/api_sample_tests/test_migrations.py +++ b/nova/tests/functional/api_sample_tests/test_migrations.py @@ -184,3 +184,110 @@ class MigrationsSamplesJsonTestV2_23(api_sample_base.ApiSampleTestBaseV21): 'migrations-get', {"instance_1": INSTANCE_UUID_1, "instance_2": INSTANCE_UUID_2}, response, 200) + + +class MigrationsSamplesJsonTestV2_59(MigrationsSamplesJsonTestV2_23): + microversion = '2.59' + scenarios = [('v2_59', {'api_major_version': 'v2.1'})] + fake_migrations = [ + # in-progress live-migration. + { + 'source_node': 'node1', + 'dest_node': 'node2', + 'source_compute': 'compute1', + 'dest_compute': 'compute2', + 'dest_host': '1.2.3.4', + 'status': 'running', + 'instance_uuid': INSTANCE_UUID_1, + 'old_instance_type_id': 1, + 'new_instance_type_id': 1, + 'migration_type': 'live-migration', + 'hidden': False, + 'created_at': datetime.datetime(2016, 0o1, 29, 11, 42, 2), + 'updated_at': datetime.datetime(2016, 0o1, 29, 11, 42, 2), + 'deleted_at': None, + 'deleted': False, + 'uuid': '12341d4b-346a-40d0-83c6-5f4f6892b650' + }, + # non in-progress live-migration. + { + 'source_node': 'node1', + 'dest_node': 'node2', + 'source_compute': 'compute1', + 'dest_compute': 'compute2', + 'dest_host': '1.2.3.4', + 'status': 'error', + 'instance_uuid': INSTANCE_UUID_1, + 'old_instance_type_id': 1, + 'new_instance_type_id': 1, + 'migration_type': 'live-migration', + 'hidden': False, + 'created_at': datetime.datetime(2016, 0o1, 29, 12, 42, 2), + 'updated_at': datetime.datetime(2016, 0o1, 29, 12, 42, 2), + 'deleted_at': None, + 'deleted': False, + 'uuid': '22341d4b-346a-40d0-83c6-5f4f6892b650' + }, + # non in-progress resize. + { + 'source_node': 'node10', + 'dest_node': 'node20', + 'source_compute': 'compute10', + 'dest_compute': 'compute20', + 'dest_host': '5.6.7.8', + 'status': 'error', + 'instance_uuid': INSTANCE_UUID_2, + 'old_instance_type_id': 5, + 'new_instance_type_id': 6, + 'migration_type': 'resize', + 'hidden': False, + 'created_at': datetime.datetime(2016, 0o6, 23, 13, 42, 2), + 'updated_at': datetime.datetime(2016, 0o6, 23, 13, 42, 2), + 'deleted_at': None, + 'deleted': False, + 'uuid': '32341d4b-346a-40d0-83c6-5f4f6892b650' + }, + # in-progress resize. + { + 'source_node': 'node10', + 'dest_node': 'node20', + 'source_compute': 'compute10', + 'dest_compute': 'compute20', + 'dest_host': '5.6.7.8', + 'status': 'migrating', + 'instance_uuid': INSTANCE_UUID_2, + 'old_instance_type_id': 5, + 'new_instance_type_id': 6, + 'migration_type': 'resize', + 'hidden': False, + 'created_at': datetime.datetime(2016, 0o6, 23, 14, 42, 2), + 'updated_at': datetime.datetime(2016, 0o6, 23, 14, 42, 2), + 'deleted_at': None, + 'deleted': False, + 'uuid': '42341d4b-346a-40d0-83c6-5f4f6892b650' + } + ] + + def test_get_migrations_with_limit(self): + response = self._do_get('os-migrations?limit=1') + self.assertEqual(200, response.status_code) + self._verify_response( + 'migrations-get-with-limit', + {"instance_2": INSTANCE_UUID_2}, response, 200) + + def test_get_migrations_with_marker(self): + response = self._do_get( + 'os-migrations?marker=22341d4b-346a-40d0-83c6-5f4f6892b650') + self.assertEqual(200, response.status_code) + self._verify_response( + 'migrations-get-with-marker', + {"instance_1": INSTANCE_UUID_1, "instance_2": INSTANCE_UUID_2}, + response, 200) + + def test_get_migrations_with_timestamp_filter(self): + response = self._do_get( + 'os-migrations?changes-since=2016-06-23T13:42:01.000000') + self.assertEqual(200, response.status_code) + self._verify_response( + 'migrations-get-with-timestamp-filter', + {"instance_2": INSTANCE_UUID_2}, response, 200) diff --git a/nova/tests/functional/api_sample_tests/test_server_migrations.py b/nova/tests/functional/api_sample_tests/test_server_migrations.py index 128a4d660d..4616e0fc9b 100644 --- a/nova/tests/functional/api_sample_tests/test_server_migrations.py +++ b/nova/tests/functional/api_sample_tests/test_server_migrations.py @@ -210,3 +210,19 @@ class ServerMigrationsSampleJsonTestV2_24(test_servers.ServersSampleBase): uri = 'servers/%s/migrations/%s' % (self.uuid, self.migration.id) response = self._do_delete(uri) self.assertEqual(400, response.status_code) + + +class ServerMigrationsSamplesJsonTestV2_59( + ServerMigrationsSamplesJsonTestV2_23 +): + ADMIN_API = True + microversion = '2.59' + scenarios = [('v2_59', {'api_major_version': 'v2.1'})] + + def setUp(self): + # Add UUIDs to the fake migrations used in the tests. + self.fake_migrations[0][ + 'uuid'] = '12341d4b-346a-40d0-83c6-5f4f6892b650' + self.fake_migrations[1][ + 'uuid'] = '22341d4b-346a-40d0-83c6-5f4f6892b650' + super(ServerMigrationsSamplesJsonTestV2_59, self).setUp() diff --git a/nova/tests/unit/api/openstack/compute/test_migrations.py b/nova/tests/unit/api/openstack/compute/test_migrations.py index cdea17e596..0ea049bc41 100644 --- a/nova/tests/unit/api/openstack/compute/test_migrations.py +++ b/nova/tests/unit/api/openstack/compute/test_migrations.py @@ -15,6 +15,8 @@ import datetime import mock +import six +from webob import exc from nova.api.openstack.compute import migrations as migrations_v21 from nova import context @@ -258,6 +260,98 @@ class MigrationsTestCaseV223(MigrationsTestCaseV21): self.assertIn('migration_type', response['migrations'][0]) +class MigrationsTestCaseV259(MigrationsTestCaseV223): + wsgi_api_version = '2.59' + + def test_index(self): + migrations = {'migrations': self.controller._output( + self.req, migrations_obj, True, True)} + + for i, mig in enumerate(migrations['migrations']): + # first item is in-progress live migration + if i == 0: + self.assertIn('links', mig) + else: + self.assertNotIn('links', mig) + + self.assertIn('migration_type', mig) + self.assertIn('id', mig) + self.assertIn('uuid', mig) + self.assertNotIn('deleted', mig) + self.assertNotIn('deleted_at', mig) + + with mock.patch.object(self.controller.compute_api, + 'get_migrations_sorted') as m_get: + m_get.return_value = migrations_obj + response = self.controller.index(self.req) + self.assertEqual(migrations, response) + self.assertIn('links', response['migrations'][0]) + self.assertIn('migration_type', response['migrations'][0]) + + @mock.patch('nova.compute.api.API.get_migrations_sorted') + def test_index_with_invalid_marker(self, mock_migrations_get): + """Tests detail paging with an invalid marker (not found).""" + mock_migrations_get.side_effect = exception.MarkerNotFound( + marker=uuids.invalid_marker) + req = fakes.HTTPRequest.blank( + '/os-migrations?marker=%s' % uuids.invalid_marker, + version=self.wsgi_api_version, use_admin_context=True) + e = self.assertRaises(exc.HTTPBadRequest, + self.controller.index, req) + self.assertEqual( + "Marker %s could not be found." % uuids.invalid_marker, + six.text_type(e)) + + def test_index_with_invalid_limit(self): + """Tests detail paging with an invalid limit.""" + req = fakes.HTTPRequest.blank( + '/os-migrations?limit=x', version=self.wsgi_api_version, + use_admin_context=True) + self.assertRaises(exception.ValidationError, + self.controller.index, req) + req = fakes.HTTPRequest.blank( + '/os-migrations?limit=-1', version=self.wsgi_api_version, + use_admin_context=True) + self.assertRaises(exception.ValidationError, + self.controller.index, req) + + def test_index_with_invalid_changes_since(self): + """Tests detail paging with an invalid changes-since value.""" + req = fakes.HTTPRequest.blank( + '/os-migrations?changes-since=wrong_time', + version=self.wsgi_api_version, use_admin_context=True) + self.assertRaises(exception.ValidationError, + self.controller.index, req) + + def test_index_with_unknown_query_param(self): + """Tests detail paging with an unknown query parameter.""" + req = fakes.HTTPRequest.blank( + '/os-migrations?foo=bar', + version=self.wsgi_api_version, use_admin_context=True) + ex = self.assertRaises(exception.ValidationError, + self.controller.index, req) + self.assertIn('Additional properties are not allowed', + six.text_type(ex)) + + @mock.patch('nova.compute.api.API.get_migrations', + return_value=objects.MigrationList()) + def test_index_with_changes_since_old_microversion(self, get_migrations): + """Tests that the changes-since query parameteris ignored before + microversion 2.59. + """ + # Also use a valid filter (instance_uuid) to make sure only + # changes-since is removed. + req = fakes.HTTPRequest.blank( + '/os-migrations?changes-since=2018-01-10T16:59:24.138939&' + 'instance_uuid=%s' % uuids.instance_uuid, + version='2.58', use_admin_context=True) + result = self.controller.index(req) + self.assertEqual({'migrations': []}, result) + get_migrations.assert_called_once_with( + req.environ['nova.context'], + {'instance_uuid': uuids.instance_uuid}) + + class MigrationsPolicyEnforcement(test.NoDBTestCase): def setUp(self): super(MigrationsPolicyEnforcement, self).setUp() @@ -281,3 +375,7 @@ class MigrationsPolicyEnforcementV223(MigrationsPolicyEnforcement): def setUp(self): super(MigrationsPolicyEnforcementV223, self).setUp() self.req = fakes.HTTPRequest.blank('', version=self.wsgi_api_version) + + +class MigrationsPolicyEnforcementV259(MigrationsPolicyEnforcementV223): + wsgi_api_version = '2.59' diff --git a/releasenotes/notes/bp-add-pagination-for-os-migrations-2f8d5d257b0c5658.yaml b/releasenotes/notes/bp-add-pagination-for-os-migrations-2f8d5d257b0c5658.yaml new file mode 100644 index 0000000000..8a99018cb5 --- /dev/null +++ b/releasenotes/notes/bp-add-pagination-for-os-migrations-2f8d5d257b0c5658.yaml @@ -0,0 +1,16 @@ +--- +features: + - | + Added pagination support for migrations, there are four changes: + + - Add pagination support and ``changes-since`` filter for os-migrations + API. Users can now use ``limit`` and ``marker`` to perform paginate query + when listing migrations. + - Users can also use ``changes-since`` filter to filter the results based + on the last time the migration record was updated. + - ``GET /os-migrations``, + ``GET /servers/{server_id}/migrations/{migration_id}`` and + ``GET /servers/{server_id}/migrations`` will now return a uuid value in + addition to the migrations id in the response. + - The query parameter schema of the ``GET /os-migrations`` API no longer + allows additional properties. \ No newline at end of file