diff --git a/api-ref/source/os-aggregates.inc b/api-ref/source/os-aggregates.inc index 26ed69d48955..67149ce29c86 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/api-ref/source/parameters.yaml b/api-ref/source/parameters.yaml index 2e887cecf4e6..26181586e950 100644 --- a/api-ref/source/parameters.yaml +++ b/api-ref/source/parameters.yaml @@ -967,6 +967,12 @@ aggregate_remove_host: in: body required: true type: object +aggregate_uuid: + description: | + The UUID of the host aggregate. + in: body + type: string + min_version: 2.41 aggregates: description: | The list of existing aggregates. 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 000000000000..4e6bdfef3f4f --- /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 000000000000..7331e06a8c0b --- /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 000000000000..82272c2936b5 --- /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 000000000000..07b9ca318cbb --- /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 000000000000..e42b053009e8 --- /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 000000000000..0af1a37a4d9c --- /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 000000000000..4054d5193dba --- /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 000000000000..0f1a40f2cc3e --- /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 000000000000..5cd1cf75ff88 --- /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 000000000000..6b68451fd921 --- /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 000000000000..9dda7f83e2b7 --- /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 000000000000..f1fde0d33829 --- /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/doc/api_samples/versions/v21-version-get-resp.json b/doc/api_samples/versions/v21-version-get-resp.json index 44662156ade4..8f9bfd8aa5a7 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.40", + "version": "2.41", "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 ecbdc6ad10bb..e424cf7a9d29 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.40", + "version": "2.41", "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 c8200a8704f0..a9a7e6c320de 100644 --- a/nova/api/openstack/api_version_request.py +++ b/nova/api/openstack/api_version_request.py @@ -97,6 +97,7 @@ REST_API_VERSION_HISTORY = """REST API Version History: provided for listing servers. * 2.39 - Deprecates image-metadata proxy API * 2.40 - Adds simple tenant usage pagination support. + * 2.41 - Return uuid attribute for aggregates. """ # The minimum and maximum versions of the API supported @@ -105,7 +106,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.40" +_MAX_API_VERSION = "2.41" DEFAULT_API_VERSION = _MIN_API_VERSION # Almost all proxy APIs which related to network, images and baremetal diff --git a/nova/api/openstack/compute/aggregates.py b/nova/api/openstack/compute/aggregates.py index dfccfb263b86..53fa17c30f95 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/api/openstack/compute/rest_api_version_history.rst b/nova/api/openstack/compute/rest_api_version_history.rst index 8ba8c0b184d8..a3917d567859 100644 --- a/nova/api/openstack/compute/rest_api_version_history.rst +++ b/nova/api/openstack/compute/rest_api_version_history.rst @@ -445,3 +445,11 @@ user documentation. `CONF.api.max_limit` to encourage the adoption of this new microversion, and circumvent the existing possibility DoS-like usage requests on systems with thousands of instances. + +2.41 +---- + + The 'uuid' attribute of an aggregate is now returned from calls to the + `/os-aggregates` endpoint. This attribute is auto-generated upon creation of + an aggregate. The `os-aggregates` API resource endpoint remains an + administrator-only API. 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 000000000000..97395bf2f22e --- /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 000000000000..63a2921cacc8 --- /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 000000000000..fc806061e88d --- /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 000000000000..dadf45cee7af --- /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 000000000000..4663e5293119 --- /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 000000000000..55e4b09346ea --- /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 000000000000..b10db24655fc --- /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 000000000000..73a158619465 --- /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 000000000000..e0a3cecc3d22 --- /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 000000000000..b90fe786e609 --- /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 000000000000..8b1f6d3ec81a --- /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 000000000000..e0a3cecc3d22 --- /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 3ea3e3d358a8..99d1d629ab88 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/functional/integrated_helpers.py b/nova/tests/functional/integrated_helpers.py index c12d7a8a6e5b..d59297fcf4dd 100644 --- a/nova/tests/functional/integrated_helpers.py +++ b/nova/tests/functional/integrated_helpers.py @@ -89,6 +89,9 @@ class _IntegratedTestBase(test.TestCase): else: self.api = self.api_fixture.api + if hasattr(self, 'microversion'): + self.api.microversion = self.microversion + self.useFixture(cast_as_call.CastAsCall(self.stubs)) self.addCleanup(nova.tests.unit.image.fake.FakeImageService_reset) diff --git a/nova/tests/unit/api/openstack/compute/test_aggregates.py b/nova/tests/unit/api/openstack/compute/test_aggregates.py index f62b409c34fa..2129c3acbd5c 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 000000000000..ba897d801d23 --- /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.