From 8b4fd32e7b8ed71e3af0d913a9d0687504f7e951 Mon Sep 17 00:00:00 2001 From: Jay Pipes Date: Mon, 26 Dec 2016 17:58:44 -0500 Subject: [PATCH] Return uuid attribute for aggregates Adds a Compute API microversion that triggers returning an aggregate's UUID field. This field is necessary for scripts that must populate the placement API with resource provider to aggregate relationships, which rely on UUIDs for global identification. APIImpact blueprint: return-uuid-from-os-aggregates-api Change-Id: I4112ccd508eb85403933fec8b52efd468e866772 Closes-bug: #1652642 --- api-ref/source/os-aggregates.inc | 7 +++ .../v2.41/aggregate-add-host-post-req.json | 5 +++ .../v2.41/aggregate-metadata-post-req.json | 9 ++++ .../v2.41/aggregate-post-req.json | 7 +++ .../v2.41/aggregate-post-resp.json | 12 +++++ .../v2.41/aggregate-remove-host-post-req.json | 5 +++ .../v2.41/aggregate-update-post-req.json | 7 +++ .../v2.41/aggregate-update-post-resp.json | 16 +++++++ .../v2.41/aggregates-add-host-post-resp.json | 18 ++++++++ .../v2.41/aggregates-get-resp.json | 16 +++++++ .../v2.41/aggregates-list-get-resp.json | 20 +++++++++ .../v2.41/aggregates-metadata-post-resp.json | 17 +++++++ .../aggregates-remove-host-post-resp.json | 16 +++++++ nova/api/openstack/compute/aggregates.py | 26 +++++------ .../aggregate-add-host-post-req.json.tpl | 5 +++ .../aggregate-metadata-post-req.json.tpl | 9 ++++ .../v2.41/aggregate-post-req.json.tpl | 7 +++ .../v2.41/aggregate-post-resp.json.tpl | 12 +++++ .../aggregate-remove-host-post-req.json.tpl | 5 +++ .../v2.41/aggregate-update-post-req.json.tpl | 7 +++ .../v2.41/aggregate-update-post-resp.json.tpl | 16 +++++++ .../aggregates-add-host-post-resp.json.tpl | 18 ++++++++ .../v2.41/aggregates-get-resp.json.tpl | 16 +++++++ .../v2.41/aggregates-list-get-resp.json.tpl | 20 +++++++++ .../aggregates-metadata-post-resp.json.tpl | 17 +++++++ .../aggregates-remove-host-post-resp.json.tpl | 16 +++++++ .../api_sample_tests/test_aggregates.py | 44 +++++++++++++++++-- .../api/openstack/compute/test_aggregates.py | 15 ++++++- ...ibute-for-aggregates-70d9f733f86fb1a3.yaml | 5 +++ 29 files changed, 376 insertions(+), 17 deletions(-) create mode 100644 doc/api_samples/os-aggregates/v2.41/aggregate-add-host-post-req.json create mode 100644 doc/api_samples/os-aggregates/v2.41/aggregate-metadata-post-req.json create mode 100644 doc/api_samples/os-aggregates/v2.41/aggregate-post-req.json create mode 100644 doc/api_samples/os-aggregates/v2.41/aggregate-post-resp.json create mode 100644 doc/api_samples/os-aggregates/v2.41/aggregate-remove-host-post-req.json create mode 100644 doc/api_samples/os-aggregates/v2.41/aggregate-update-post-req.json create mode 100644 doc/api_samples/os-aggregates/v2.41/aggregate-update-post-resp.json create mode 100644 doc/api_samples/os-aggregates/v2.41/aggregates-add-host-post-resp.json create mode 100644 doc/api_samples/os-aggregates/v2.41/aggregates-get-resp.json create mode 100644 doc/api_samples/os-aggregates/v2.41/aggregates-list-get-resp.json create mode 100644 doc/api_samples/os-aggregates/v2.41/aggregates-metadata-post-resp.json create mode 100644 doc/api_samples/os-aggregates/v2.41/aggregates-remove-host-post-resp.json create mode 100644 nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregate-add-host-post-req.json.tpl create mode 100644 nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregate-metadata-post-req.json.tpl create mode 100644 nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregate-post-req.json.tpl create mode 100644 nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregate-post-resp.json.tpl create mode 100644 nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregate-remove-host-post-req.json.tpl create mode 100644 nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregate-update-post-req.json.tpl create mode 100644 nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregate-update-post-resp.json.tpl create mode 100644 nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregates-add-host-post-resp.json.tpl create mode 100644 nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregates-get-resp.json.tpl create mode 100644 nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregates-list-get-resp.json.tpl create mode 100644 nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregates-metadata-post-resp.json.tpl create mode 100644 nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregates-remove-host-post-resp.json.tpl create mode 100644 releasenotes/notes/return-uuid-attribute-for-aggregates-70d9f733f86fb1a3.yaml diff --git a/api-ref/source/os-aggregates.inc b/api-ref/source/os-aggregates.inc index 26ed69d48..67149ce29 100644 --- a/api-ref/source/os-aggregates.inc +++ b/api-ref/source/os-aggregates.inc @@ -34,6 +34,7 @@ Response - metadata: aggregate_metadata - name: aggregate_name - updated_at: updated_consider_null + - uuid: aggregate_uuid **Example List Aggregates: JSON response** @@ -79,6 +80,7 @@ Response - id: aggregate_id_body - name: aggregate_name - updated_at: updated_consider_null + - uuid: aggregate_uuid **Example Create Aggregate: JSON response** @@ -118,6 +120,7 @@ Response - metadata: aggregate_metadata - name: aggregate_name - updated_at: updated_consider_null + - uuid: aggregate_uuid **Example Show Aggregate Details: JSON response** @@ -168,6 +171,7 @@ Response - metadata: aggregate_metadata - name: aggregate_name - updated_at: updated_consider_null + - uuid: aggregate_uuid **Example Update Aggregate: JSON response** @@ -240,6 +244,7 @@ Response - metadata: aggregate_metadata - name: aggregate_name - updated_at: updated_consider_null + - uuid: aggregate_uuid **Example Add Host: JSON response** @@ -289,6 +294,7 @@ Response - metadata: aggregate_metadata - name: aggregate_name - updated_at: updated_consider_null + - uuid: aggregate_uuid **Example Remove Host: JSON response** @@ -338,6 +344,7 @@ Response - metadata: aggregate_metadata - name: aggregate_name - updated_at: updated_consider_null + - uuid: aggregate_uuid **Example Create Or Update Aggregate Metadata: JSON response** diff --git a/doc/api_samples/os-aggregates/v2.41/aggregate-add-host-post-req.json b/doc/api_samples/os-aggregates/v2.41/aggregate-add-host-post-req.json new file mode 100644 index 000000000..4e6bdfef3 --- /dev/null +++ b/doc/api_samples/os-aggregates/v2.41/aggregate-add-host-post-req.json @@ -0,0 +1,5 @@ +{ + "add_host": { + "host": "compute" + } +} \ No newline at end of file diff --git a/doc/api_samples/os-aggregates/v2.41/aggregate-metadata-post-req.json b/doc/api_samples/os-aggregates/v2.41/aggregate-metadata-post-req.json new file mode 100644 index 000000000..7331e06a8 --- /dev/null +++ b/doc/api_samples/os-aggregates/v2.41/aggregate-metadata-post-req.json @@ -0,0 +1,9 @@ +{ + "set_metadata": + { + "metadata": + { + "key": "value" + } + } +} \ No newline at end of file diff --git a/doc/api_samples/os-aggregates/v2.41/aggregate-post-req.json b/doc/api_samples/os-aggregates/v2.41/aggregate-post-req.json new file mode 100644 index 000000000..82272c293 --- /dev/null +++ b/doc/api_samples/os-aggregates/v2.41/aggregate-post-req.json @@ -0,0 +1,7 @@ +{ + "aggregate": + { + "name": "name", + "availability_zone": "nova" + } +} \ No newline at end of file diff --git a/doc/api_samples/os-aggregates/v2.41/aggregate-post-resp.json b/doc/api_samples/os-aggregates/v2.41/aggregate-post-resp.json new file mode 100644 index 000000000..07b9ca318 --- /dev/null +++ b/doc/api_samples/os-aggregates/v2.41/aggregate-post-resp.json @@ -0,0 +1,12 @@ +{ + "aggregate": { + "availability_zone": "nova", + "created_at": "2016-12-27T22:51:32.877711", + "deleted": false, + "deleted_at": null, + "id": 1, + "name": "name", + "updated_at": null, + "uuid": "86a0da0e-9f0c-4f51-a1e0-3c25edab3783" + } +} \ No newline at end of file diff --git a/doc/api_samples/os-aggregates/v2.41/aggregate-remove-host-post-req.json b/doc/api_samples/os-aggregates/v2.41/aggregate-remove-host-post-req.json new file mode 100644 index 000000000..e42b05300 --- /dev/null +++ b/doc/api_samples/os-aggregates/v2.41/aggregate-remove-host-post-req.json @@ -0,0 +1,5 @@ +{ + "remove_host": { + "host": "compute" + } +} \ No newline at end of file diff --git a/doc/api_samples/os-aggregates/v2.41/aggregate-update-post-req.json b/doc/api_samples/os-aggregates/v2.41/aggregate-update-post-req.json new file mode 100644 index 000000000..0af1a37a4 --- /dev/null +++ b/doc/api_samples/os-aggregates/v2.41/aggregate-update-post-req.json @@ -0,0 +1,7 @@ +{ + "aggregate": + { + "name": "newname", + "availability_zone": "nova2" + } +} \ No newline at end of file diff --git a/doc/api_samples/os-aggregates/v2.41/aggregate-update-post-resp.json b/doc/api_samples/os-aggregates/v2.41/aggregate-update-post-resp.json new file mode 100644 index 000000000..4054d5193 --- /dev/null +++ b/doc/api_samples/os-aggregates/v2.41/aggregate-update-post-resp.json @@ -0,0 +1,16 @@ +{ + "aggregate": { + "availability_zone": "nova2", + "created_at": "2016-12-27T23:47:32.897139", + "deleted": false, + "deleted_at": null, + "hosts": [], + "id": 1, + "metadata": { + "availability_zone": "nova2" + }, + "name": "newname", + "updated_at": "2016-12-27T23:47:33.067180", + "uuid": "6f74e3f3-df28-48f3-98e1-ac941b1c5e43" + } +} \ No newline at end of file diff --git a/doc/api_samples/os-aggregates/v2.41/aggregates-add-host-post-resp.json b/doc/api_samples/os-aggregates/v2.41/aggregates-add-host-post-resp.json new file mode 100644 index 000000000..0f1a40f2c --- /dev/null +++ b/doc/api_samples/os-aggregates/v2.41/aggregates-add-host-post-resp.json @@ -0,0 +1,18 @@ +{ + "aggregate": { + "availability_zone": "nova", + "created_at": "2016-12-27T23:47:30.594805", + "deleted": false, + "deleted_at": null, + "hosts": [ + "compute" + ], + "id": 1, + "metadata": { + "availability_zone": "nova" + }, + "name": "name", + "updated_at": null, + "uuid": "d1842372-89c5-4fbd-ad5a-5d2e16c85456" + } +} \ No newline at end of file diff --git a/doc/api_samples/os-aggregates/v2.41/aggregates-get-resp.json b/doc/api_samples/os-aggregates/v2.41/aggregates-get-resp.json new file mode 100644 index 000000000..5cd1cf75f --- /dev/null +++ b/doc/api_samples/os-aggregates/v2.41/aggregates-get-resp.json @@ -0,0 +1,16 @@ +{ + "aggregate": { + "availability_zone": "nova", + "created_at": "2016-12-27T23:47:30.563527", + "deleted": false, + "deleted_at": null, + "hosts": [], + "id": 1, + "metadata": { + "availability_zone": "nova" + }, + "name": "name", + "updated_at": null, + "uuid": "fd0a5b12-7e8d-469d-bfd5-64a6823e7407" + } +} \ No newline at end of file diff --git a/doc/api_samples/os-aggregates/v2.41/aggregates-list-get-resp.json b/doc/api_samples/os-aggregates/v2.41/aggregates-list-get-resp.json new file mode 100644 index 000000000..6b68451fd --- /dev/null +++ b/doc/api_samples/os-aggregates/v2.41/aggregates-list-get-resp.json @@ -0,0 +1,20 @@ +{ + "aggregates": [ + { + "availability_zone": "nova", + "created_at": "2016-12-27T23:47:32.911515", + "deleted": false, + "deleted_at": null, + "hosts": [ + "compute" + ], + "id": 1, + "metadata": { + "availability_zone": "nova" + }, + "name": "name", + "updated_at": null, + "uuid": "6ba28ba7-f29b-45cc-a30b-6e3a40c2fb14" + } + ] +} \ No newline at end of file diff --git a/doc/api_samples/os-aggregates/v2.41/aggregates-metadata-post-resp.json b/doc/api_samples/os-aggregates/v2.41/aggregates-metadata-post-resp.json new file mode 100644 index 000000000..9dda7f83e --- /dev/null +++ b/doc/api_samples/os-aggregates/v2.41/aggregates-metadata-post-resp.json @@ -0,0 +1,17 @@ +{ + "aggregate": { + "availability_zone": "nova", + "created_at": "2016-12-27T23:59:18.623100", + "deleted": false, + "deleted_at": null, + "hosts": [], + "id": 1, + "metadata": { + "availability_zone": "nova", + "key": "value" + }, + "name": "name", + "updated_at": "2016-12-27T23:59:18.723348", + "uuid": "26002bdb-62cc-41bd-813a-0ad22db32625" + } +} \ No newline at end of file diff --git a/doc/api_samples/os-aggregates/v2.41/aggregates-remove-host-post-resp.json b/doc/api_samples/os-aggregates/v2.41/aggregates-remove-host-post-resp.json new file mode 100644 index 000000000..f1fde0d33 --- /dev/null +++ b/doc/api_samples/os-aggregates/v2.41/aggregates-remove-host-post-resp.json @@ -0,0 +1,16 @@ +{ + "aggregate": { + "availability_zone": "nova", + "created_at": "2016-12-27T23:47:30.594805", + "deleted": false, + "deleted_at": null, + "hosts": [], + "id": 1, + "metadata": { + "availability_zone": "nova" + }, + "name": "name", + "updated_at": null, + "uuid": "d1842372-89c5-4fbd-ad5a-5d2e16c85456" + } +} \ No newline at end of file diff --git a/nova/api/openstack/compute/aggregates.py b/nova/api/openstack/compute/aggregates.py index dfccfb263..53fa17c30 100644 --- a/nova/api/openstack/compute/aggregates.py +++ b/nova/api/openstack/compute/aggregates.py @@ -19,6 +19,7 @@ import datetime 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 aggregates from nova.api.openstack import extensions @@ -47,7 +48,7 @@ class AggregateController(wsgi.Controller): context = _get_context(req) context.can(aggr_policies.POLICY_ROOT % 'index') aggregates = self.api.get_aggregate_list(context) - return {'aggregates': [self._marshall_aggregate(a)['aggregate'] + return {'aggregates': [self._marshall_aggregate(req, a)['aggregate'] for a in aggregates]} # NOTE(gmann): Returns 200 for backwards compatibility but should be 201 @@ -77,7 +78,7 @@ class AggregateController(wsgi.Controller): except exception.InvalidAggregateAction as e: raise exc.HTTPBadRequest(explanation=e.format_message()) - agg = self._marshall_aggregate(aggregate) + agg = self._marshall_aggregate(req, aggregate) # To maintain the same API result as before the changes for returning # nova objects were made. @@ -95,7 +96,7 @@ class AggregateController(wsgi.Controller): aggregate = self.api.get_aggregate(context, id) except exception.AggregateNotFound as e: raise exc.HTTPNotFound(explanation=e.format_message()) - return self._marshall_aggregate(aggregate) + return self._marshall_aggregate(req, aggregate) @extensions.expected_errors((400, 404, 409)) @validation.schema(aggregates.update_v20, '2.0', '2.0') @@ -117,7 +118,7 @@ class AggregateController(wsgi.Controller): except exception.InvalidAggregateAction as e: raise exc.HTTPBadRequest(explanation=e.format_message()) - return self._marshall_aggregate(aggregate) + return self._marshall_aggregate(req, aggregate) # NOTE(gmann): Returns 200 for backwards compatibility but should be 204 # as this operation complete the deletion of aggregate resource and return @@ -154,7 +155,7 @@ class AggregateController(wsgi.Controller): except (exception.AggregateHostExists, exception.InvalidAggregateAction) as e: raise exc.HTTPConflict(explanation=e.format_message()) - return self._marshall_aggregate(aggregate) + return self._marshall_aggregate(req, aggregate) # NOTE(gmann): Returns 200 for backwards compatibility but should be 202 # for representing async API as this API just accepts the request and @@ -179,7 +180,7 @@ class AggregateController(wsgi.Controller): msg = _('Cannot remove host %(host)s in aggregate %(id)s') % { 'host': host, 'id': id} raise exc.HTTPConflict(explanation=msg) - return self._marshall_aggregate(aggregate) + return self._marshall_aggregate(req, aggregate) @extensions.expected_errors((400, 404)) @wsgi.action('set_metadata') @@ -198,18 +199,19 @@ class AggregateController(wsgi.Controller): except exception.InvalidAggregateAction as e: raise exc.HTTPBadRequest(explanation=e.format_message()) - return self._marshall_aggregate(aggregate) + return self._marshall_aggregate(req, aggregate) - def _marshall_aggregate(self, aggregate): + def _marshall_aggregate(self, req, aggregate): _aggregate = {} - for key, value in self._build_aggregate_items(aggregate): + for key, value in self._build_aggregate_items(req, aggregate): # NOTE(danms): The original API specified non-TZ-aware timestamps if isinstance(value, datetime.datetime): value = value.replace(tzinfo=None) _aggregate[key] = value return {"aggregate": _aggregate} - def _build_aggregate_items(self, aggregate): + def _build_aggregate_items(self, req, aggregate): + show_uuid = api_version_request.is_supported(req, min_version="2.41") keys = aggregate.obj_fields # NOTE(rlrossit): Within the compute API, metadata will always be # set on the aggregate object (at a minimum to {}). Because of this, @@ -217,11 +219,9 @@ class AggregateController(wsgi.Controller): # case it is only ['availability_zone']) without worrying about # lazy-loading an unset variable for key in keys: - # NOTE(danms): Skip the uuid field because we have no microversion - # to expose it if ((aggregate.obj_attr_is_set(key) or key in aggregate.obj_extra_fields) and - key != 'uuid'): + (show_uuid or key != 'uuid')): yield key, getattr(aggregate, key) diff --git a/nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregate-add-host-post-req.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregate-add-host-post-req.json.tpl new file mode 100644 index 000000000..97395bf2f --- /dev/null +++ b/nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregate-add-host-post-req.json.tpl @@ -0,0 +1,5 @@ +{ + "add_host": { + "host": "%(host_name)s" + } +} diff --git a/nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregate-metadata-post-req.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregate-metadata-post-req.json.tpl new file mode 100644 index 000000000..63a2921ca --- /dev/null +++ b/nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregate-metadata-post-req.json.tpl @@ -0,0 +1,9 @@ +{ + "set_metadata": + { + "metadata": + { + "key": "value" + } + } +} diff --git a/nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregate-post-req.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregate-post-req.json.tpl new file mode 100644 index 000000000..fc806061e --- /dev/null +++ b/nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregate-post-req.json.tpl @@ -0,0 +1,7 @@ +{ + "aggregate": + { + "name": "name", + "availability_zone": "nova" + } +} diff --git a/nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregate-post-resp.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregate-post-resp.json.tpl new file mode 100644 index 000000000..dadf45cee --- /dev/null +++ b/nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregate-post-resp.json.tpl @@ -0,0 +1,12 @@ +{ + "aggregate": { + "availability_zone": "nova", + "created_at": "%(strtime)s", + "deleted": false, + "deleted_at": null, + "id": %(aggregate_id)s, + "name": "name", + "updated_at": null, + "uuid": "%(uuid)s" + } +} diff --git a/nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregate-remove-host-post-req.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregate-remove-host-post-req.json.tpl new file mode 100644 index 000000000..4663e5293 --- /dev/null +++ b/nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregate-remove-host-post-req.json.tpl @@ -0,0 +1,5 @@ +{ + "remove_host": { + "host": "%(host_name)s" + } +} diff --git a/nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregate-update-post-req.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregate-update-post-req.json.tpl new file mode 100644 index 000000000..55e4b0934 --- /dev/null +++ b/nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregate-update-post-req.json.tpl @@ -0,0 +1,7 @@ +{ + "aggregate": + { + "name": "newname", + "availability_zone": "nova2" + } +} diff --git a/nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregate-update-post-resp.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregate-update-post-resp.json.tpl new file mode 100644 index 000000000..b10db2465 --- /dev/null +++ b/nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregate-update-post-resp.json.tpl @@ -0,0 +1,16 @@ +{ + "aggregate": { + "availability_zone": "nova2", + "created_at": "%(strtime)s", + "deleted": false, + "deleted_at": null, + "hosts": [], + "id": 1, + "metadata": { + "availability_zone": "nova2" + }, + "name": "newname", + "updated_at": "%(strtime)s", + "uuid": "%(uuid)s" + } +} diff --git a/nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregates-add-host-post-resp.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregates-add-host-post-resp.json.tpl new file mode 100644 index 000000000..73a158619 --- /dev/null +++ b/nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregates-add-host-post-resp.json.tpl @@ -0,0 +1,18 @@ +{ + "aggregate": { + "availability_zone": "nova", + "created_at": "%(strtime)s", + "deleted": false, + "deleted_at": null, + "hosts": [ + "%(compute_host)s" + ], + "id": 1, + "metadata": { + "availability_zone": "nova" + }, + "name": "name", + "updated_at": null, + "uuid": "%(uuid)s" + } +} diff --git a/nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregates-get-resp.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregates-get-resp.json.tpl new file mode 100644 index 000000000..e0a3cecc3 --- /dev/null +++ b/nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregates-get-resp.json.tpl @@ -0,0 +1,16 @@ +{ + "aggregate": { + "availability_zone": "nova", + "created_at": "%(strtime)s", + "deleted": false, + "deleted_at": null, + "hosts": [], + "id": 1, + "metadata": { + "availability_zone": "nova" + }, + "name": "name", + "updated_at": null, + "uuid": "%(uuid)s" + } +} diff --git a/nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregates-list-get-resp.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregates-list-get-resp.json.tpl new file mode 100644 index 000000000..b90fe786e --- /dev/null +++ b/nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregates-list-get-resp.json.tpl @@ -0,0 +1,20 @@ +{ + "aggregates": [ + { + "availability_zone": "nova", + "created_at": "%(strtime)s", + "deleted": false, + "deleted_at": null, + "hosts": [ + "%(compute_host)s" + ], + "id": 1, + "metadata": { + "availability_zone": "nova" + }, + "name": "name", + "updated_at": null, + "uuid": "%(uuid)s" + } + ] +} diff --git a/nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregates-metadata-post-resp.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregates-metadata-post-resp.json.tpl new file mode 100644 index 000000000..8b1f6d3ec --- /dev/null +++ b/nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregates-metadata-post-resp.json.tpl @@ -0,0 +1,17 @@ +{ + "aggregate": { + "availability_zone": "nova", + "created_at": "%(strtime)s", + "deleted": false, + "deleted_at": null, + "hosts": [], + "id": 1, + "metadata": { + "availability_zone": "nova", + "key": "value" + }, + "name": "name", + "updated_at": %(strtime)s, + "uuid": "%(uuid)s" + } +} diff --git a/nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregates-remove-host-post-resp.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregates-remove-host-post-resp.json.tpl new file mode 100644 index 000000000..e0a3cecc3 --- /dev/null +++ b/nova/tests/functional/api_sample_tests/api_samples/os-aggregates/v2.41/aggregates-remove-host-post-resp.json.tpl @@ -0,0 +1,16 @@ +{ + "aggregate": { + "availability_zone": "nova", + "created_at": "%(strtime)s", + "deleted": false, + "deleted_at": null, + "hosts": [], + "id": 1, + "metadata": { + "availability_zone": "nova" + }, + "name": "name", + "updated_at": null, + "uuid": "%(uuid)s" + } +} diff --git a/nova/tests/functional/api_sample_tests/test_aggregates.py b/nova/tests/functional/api_sample_tests/test_aggregates.py index 3ea3e3d35..99d1d629a 100644 --- a/nova/tests/functional/api_sample_tests/test_aggregates.py +++ b/nova/tests/functional/api_sample_tests/test_aggregates.py @@ -13,12 +13,18 @@ # License for the specific language governing permissions and limitations # under the License. +from oslo_serialization import jsonutils + from nova.tests.functional.api_sample_tests import api_sample_base class AggregatesSampleJsonTest(api_sample_base.ApiSampleTestBaseV21): ADMIN_API = True sample_dir = "os-aggregates" + # extra_subs is a noop in the base v2.1 test class; it's used to sub in + # additional details for response verification of actions performed on an + # existing aggregate. + extra_subs = {} def _test_aggregate_create(self): subs = { @@ -37,6 +43,7 @@ class AggregatesSampleJsonTest(api_sample_base.ApiSampleTestBaseV21): } response = self._do_post('os-aggregates/%s/action' % aggregate_id, 'aggregate-add-host-post-req', subs) + subs.update(self.extra_subs) self._verify_response('aggregates-add-host-post-resp', subs, response, 200) @@ -49,14 +56,15 @@ class AggregatesSampleJsonTest(api_sample_base.ApiSampleTestBaseV21): def test_aggregate_get(self): agg_id = self._test_aggregate_create() response = self._do_get('os-aggregates/%s' % agg_id) - self._verify_response('aggregates-get-resp', {}, response, 200) + self._verify_response('aggregates-get-resp', self.extra_subs, + response, 200) def test_add_metadata(self): agg_id = self._test_aggregate_create() response = self._do_post('os-aggregates/%s/action' % agg_id, 'aggregate-metadata-post-req', {'action': 'set_metadata'}) - self._verify_response('aggregates-metadata-post-resp', {}, + self._verify_response('aggregates-metadata-post-resp', self.extra_subs, response, 200) def test_add_host(self): @@ -70,6 +78,7 @@ class AggregatesSampleJsonTest(api_sample_base.ApiSampleTestBaseV21): } response = self._do_post('os-aggregates/1/action', 'aggregate-remove-host-post-req', subs) + subs.update(self.extra_subs) self._verify_response('aggregates-remove-host-post-resp', subs, response, 200) @@ -78,4 +87,33 @@ class AggregatesSampleJsonTest(api_sample_base.ApiSampleTestBaseV21): response = self._do_put('os-aggregates/%s' % aggregate_id, 'aggregate-update-post-req', {}) self._verify_response('aggregate-update-post-resp', - {}, response, 200) + self.extra_subs, response, 200) + + +class AggregatesV2_41_SampleJsonTest(AggregatesSampleJsonTest): + microversion = '2.41' + scenarios = [ + ( + "v2_41", { + 'api_major_version': 'v2.1', + }, + ) + ] + + def _test_aggregate_create(self): + subs = { + "aggregate_id": '(?P\d+)', + } + response = self._do_post('os-aggregates', 'aggregate-post-req', subs) + # This feels like cheating since we're getting the uuid from the + # response before we even validate that it exists in the response based + # on the sample, but we'll fail with a KeyError if it doesn't which is + # maybe good enough. Alternatively we have to mock out the DB API + # to return a fake aggregate with a hard-coded uuid that matches the + # API sample which isn't fun either. + subs['uuid'] = jsonutils.loads(response.content)['aggregate']['uuid'] + # save off the uuid for subs validation on other actions performed + # on this aggregate + self.extra_subs['uuid'] = subs['uuid'] + return self._verify_response('aggregate-post-resp', + subs, response, 200) diff --git a/nova/tests/unit/api/openstack/compute/test_aggregates.py b/nova/tests/unit/api/openstack/compute/test_aggregates.py index f62b409c3..2129c3acb 100644 --- a/nova/tests/unit/api/openstack/compute/test_aggregates.py +++ b/nova/tests/unit/api/openstack/compute/test_aggregates.py @@ -18,6 +18,7 @@ import mock from webob import exc +from nova.api.openstack import api_version_request from nova.api.openstack.compute import aggregates as aggregates_v21 from nova.compute import api as compute_api from nova import context @@ -743,11 +744,23 @@ class AggregateTestCaseV21(test.NoDBTestCase): 'metadata': {'foo': 'bar', 'availability_zone': 'nova'}, 'hosts': ['host1', 'host2']} agg_obj = _make_agg_obj(agg) - marshalled_agg = self.controller._marshall_aggregate(agg_obj) # _marshall_aggregate() puts all fields and obj_extra_fields in the # top-level dict, so we need to put availability_zone at the top also agg['availability_zone'] = 'nova' + + avr_v240 = api_version_request.APIVersionRequest("2.40") + avr_v241 = api_version_request.APIVersionRequest("2.41") + + req = mock.MagicMock(api_version_request=avr_v241) + marshalled_agg = self.controller._marshall_aggregate(req, agg_obj) + + self.assertEqual(agg, marshalled_agg['aggregate']) + + req = mock.MagicMock(api_version_request=avr_v240) + marshalled_agg = self.controller._marshall_aggregate(req, agg_obj) + + # UUID isn't in microversion 2.40 and before del agg['uuid'] self.assertEqual(agg, marshalled_agg['aggregate']) diff --git a/releasenotes/notes/return-uuid-attribute-for-aggregates-70d9f733f86fb1a3.yaml b/releasenotes/notes/return-uuid-attribute-for-aggregates-70d9f733f86fb1a3.yaml new file mode 100644 index 000000000..ba897d801 --- /dev/null +++ b/releasenotes/notes/return-uuid-attribute-for-aggregates-70d9f733f86fb1a3.yaml @@ -0,0 +1,5 @@ +--- +features: + - A new 2.41 microversion was added to the Compute API. Users specifying this + microversion will now see the 'uuid' attribute of aggregates when calling + the `os-aggregates` REST API endpoint.