Merge "Return uuid attribute for aggregates"
This commit is contained in:
commit
9b918199d8
|
@ -34,6 +34,7 @@ Response
|
||||||
- metadata: aggregate_metadata
|
- metadata: aggregate_metadata
|
||||||
- name: aggregate_name
|
- name: aggregate_name
|
||||||
- updated_at: updated_consider_null
|
- updated_at: updated_consider_null
|
||||||
|
- uuid: aggregate_uuid
|
||||||
|
|
||||||
**Example List Aggregates: JSON response**
|
**Example List Aggregates: JSON response**
|
||||||
|
|
||||||
|
@ -79,6 +80,7 @@ Response
|
||||||
- id: aggregate_id_body
|
- id: aggregate_id_body
|
||||||
- name: aggregate_name
|
- name: aggregate_name
|
||||||
- updated_at: updated_consider_null
|
- updated_at: updated_consider_null
|
||||||
|
- uuid: aggregate_uuid
|
||||||
|
|
||||||
**Example Create Aggregate: JSON response**
|
**Example Create Aggregate: JSON response**
|
||||||
|
|
||||||
|
@ -118,6 +120,7 @@ Response
|
||||||
- metadata: aggregate_metadata
|
- metadata: aggregate_metadata
|
||||||
- name: aggregate_name
|
- name: aggregate_name
|
||||||
- updated_at: updated_consider_null
|
- updated_at: updated_consider_null
|
||||||
|
- uuid: aggregate_uuid
|
||||||
|
|
||||||
**Example Show Aggregate Details: JSON response**
|
**Example Show Aggregate Details: JSON response**
|
||||||
|
|
||||||
|
@ -168,6 +171,7 @@ Response
|
||||||
- metadata: aggregate_metadata
|
- metadata: aggregate_metadata
|
||||||
- name: aggregate_name
|
- name: aggregate_name
|
||||||
- updated_at: updated_consider_null
|
- updated_at: updated_consider_null
|
||||||
|
- uuid: aggregate_uuid
|
||||||
|
|
||||||
**Example Update Aggregate: JSON response**
|
**Example Update Aggregate: JSON response**
|
||||||
|
|
||||||
|
@ -240,6 +244,7 @@ Response
|
||||||
- metadata: aggregate_metadata
|
- metadata: aggregate_metadata
|
||||||
- name: aggregate_name
|
- name: aggregate_name
|
||||||
- updated_at: updated_consider_null
|
- updated_at: updated_consider_null
|
||||||
|
- uuid: aggregate_uuid
|
||||||
|
|
||||||
**Example Add Host: JSON response**
|
**Example Add Host: JSON response**
|
||||||
|
|
||||||
|
@ -289,6 +294,7 @@ Response
|
||||||
- metadata: aggregate_metadata
|
- metadata: aggregate_metadata
|
||||||
- name: aggregate_name
|
- name: aggregate_name
|
||||||
- updated_at: updated_consider_null
|
- updated_at: updated_consider_null
|
||||||
|
- uuid: aggregate_uuid
|
||||||
|
|
||||||
**Example Remove Host: JSON response**
|
**Example Remove Host: JSON response**
|
||||||
|
|
||||||
|
@ -338,6 +344,7 @@ Response
|
||||||
- metadata: aggregate_metadata
|
- metadata: aggregate_metadata
|
||||||
- name: aggregate_name
|
- name: aggregate_name
|
||||||
- updated_at: updated_consider_null
|
- updated_at: updated_consider_null
|
||||||
|
- uuid: aggregate_uuid
|
||||||
|
|
||||||
**Example Create Or Update Aggregate Metadata: JSON response**
|
**Example Create Or Update Aggregate Metadata: JSON response**
|
||||||
|
|
||||||
|
|
|
@ -967,6 +967,12 @@ aggregate_remove_host:
|
||||||
in: body
|
in: body
|
||||||
required: true
|
required: true
|
||||||
type: object
|
type: object
|
||||||
|
aggregate_uuid:
|
||||||
|
description: |
|
||||||
|
The UUID of the host aggregate.
|
||||||
|
in: body
|
||||||
|
type: string
|
||||||
|
min_version: 2.41
|
||||||
aggregates:
|
aggregates:
|
||||||
description: |
|
description: |
|
||||||
The list of existing aggregates.
|
The list of existing aggregates.
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"add_host": {
|
||||||
|
"host": "compute"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"set_metadata":
|
||||||
|
{
|
||||||
|
"metadata":
|
||||||
|
{
|
||||||
|
"key": "value"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"aggregate":
|
||||||
|
{
|
||||||
|
"name": "name",
|
||||||
|
"availability_zone": "nova"
|
||||||
|
}
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"remove_host": {
|
||||||
|
"host": "compute"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"aggregate":
|
||||||
|
{
|
||||||
|
"name": "newname",
|
||||||
|
"availability_zone": "nova2"
|
||||||
|
}
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,7 +19,7 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"status": "CURRENT",
|
"status": "CURRENT",
|
||||||
"version": "2.40",
|
"version": "2.41",
|
||||||
"min_version": "2.1",
|
"min_version": "2.1",
|
||||||
"updated": "2013-07-23T11:33:21Z"
|
"updated": "2013-07-23T11:33:21Z"
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"status": "CURRENT",
|
"status": "CURRENT",
|
||||||
"version": "2.40",
|
"version": "2.41",
|
||||||
"min_version": "2.1",
|
"min_version": "2.1",
|
||||||
"updated": "2013-07-23T11:33:21Z"
|
"updated": "2013-07-23T11:33:21Z"
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,6 +97,7 @@ REST_API_VERSION_HISTORY = """REST API Version History:
|
||||||
provided for listing servers.
|
provided for listing servers.
|
||||||
* 2.39 - Deprecates image-metadata proxy API
|
* 2.39 - Deprecates image-metadata proxy API
|
||||||
* 2.40 - Adds simple tenant usage pagination support.
|
* 2.40 - Adds simple tenant usage pagination support.
|
||||||
|
* 2.41 - Return uuid attribute for aggregates.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# The minimum and maximum versions of the API supported
|
# 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
|
# Note(cyeoh): This only applies for the v2.1 API once microversions
|
||||||
# support is fully merged. It does not affect the V2 API.
|
# support is fully merged. It does not affect the V2 API.
|
||||||
_MIN_API_VERSION = "2.1"
|
_MIN_API_VERSION = "2.1"
|
||||||
_MAX_API_VERSION = "2.40"
|
_MAX_API_VERSION = "2.41"
|
||||||
DEFAULT_API_VERSION = _MIN_API_VERSION
|
DEFAULT_API_VERSION = _MIN_API_VERSION
|
||||||
|
|
||||||
# Almost all proxy APIs which related to network, images and baremetal
|
# Almost all proxy APIs which related to network, images and baremetal
|
||||||
|
|
|
@ -19,6 +19,7 @@ import datetime
|
||||||
|
|
||||||
from webob import exc
|
from webob import exc
|
||||||
|
|
||||||
|
from nova.api.openstack import api_version_request
|
||||||
from nova.api.openstack import common
|
from nova.api.openstack import common
|
||||||
from nova.api.openstack.compute.schemas import aggregates
|
from nova.api.openstack.compute.schemas import aggregates
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
|
@ -47,7 +48,7 @@ class AggregateController(wsgi.Controller):
|
||||||
context = _get_context(req)
|
context = _get_context(req)
|
||||||
context.can(aggr_policies.POLICY_ROOT % 'index')
|
context.can(aggr_policies.POLICY_ROOT % 'index')
|
||||||
aggregates = self.api.get_aggregate_list(context)
|
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]}
|
for a in aggregates]}
|
||||||
|
|
||||||
# NOTE(gmann): Returns 200 for backwards compatibility but should be 201
|
# NOTE(gmann): Returns 200 for backwards compatibility but should be 201
|
||||||
|
@ -77,7 +78,7 @@ class AggregateController(wsgi.Controller):
|
||||||
except exception.InvalidAggregateAction as e:
|
except exception.InvalidAggregateAction as e:
|
||||||
raise exc.HTTPBadRequest(explanation=e.format_message())
|
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
|
# To maintain the same API result as before the changes for returning
|
||||||
# nova objects were made.
|
# nova objects were made.
|
||||||
|
@ -95,7 +96,7 @@ class AggregateController(wsgi.Controller):
|
||||||
aggregate = self.api.get_aggregate(context, id)
|
aggregate = self.api.get_aggregate(context, id)
|
||||||
except exception.AggregateNotFound as e:
|
except exception.AggregateNotFound as e:
|
||||||
raise exc.HTTPNotFound(explanation=e.format_message())
|
raise exc.HTTPNotFound(explanation=e.format_message())
|
||||||
return self._marshall_aggregate(aggregate)
|
return self._marshall_aggregate(req, aggregate)
|
||||||
|
|
||||||
@extensions.expected_errors((400, 404, 409))
|
@extensions.expected_errors((400, 404, 409))
|
||||||
@validation.schema(aggregates.update_v20, '2.0', '2.0')
|
@validation.schema(aggregates.update_v20, '2.0', '2.0')
|
||||||
|
@ -117,7 +118,7 @@ class AggregateController(wsgi.Controller):
|
||||||
except exception.InvalidAggregateAction as e:
|
except exception.InvalidAggregateAction as e:
|
||||||
raise exc.HTTPBadRequest(explanation=e.format_message())
|
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
|
# NOTE(gmann): Returns 200 for backwards compatibility but should be 204
|
||||||
# as this operation complete the deletion of aggregate resource and return
|
# as this operation complete the deletion of aggregate resource and return
|
||||||
|
@ -154,7 +155,7 @@ class AggregateController(wsgi.Controller):
|
||||||
except (exception.AggregateHostExists,
|
except (exception.AggregateHostExists,
|
||||||
exception.InvalidAggregateAction) as e:
|
exception.InvalidAggregateAction) as e:
|
||||||
raise exc.HTTPConflict(explanation=e.format_message())
|
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
|
# NOTE(gmann): Returns 200 for backwards compatibility but should be 202
|
||||||
# for representing async API as this API just accepts the request and
|
# 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') % {
|
msg = _('Cannot remove host %(host)s in aggregate %(id)s') % {
|
||||||
'host': host, 'id': id}
|
'host': host, 'id': id}
|
||||||
raise exc.HTTPConflict(explanation=msg)
|
raise exc.HTTPConflict(explanation=msg)
|
||||||
return self._marshall_aggregate(aggregate)
|
return self._marshall_aggregate(req, aggregate)
|
||||||
|
|
||||||
@extensions.expected_errors((400, 404))
|
@extensions.expected_errors((400, 404))
|
||||||
@wsgi.action('set_metadata')
|
@wsgi.action('set_metadata')
|
||||||
|
@ -198,18 +199,19 @@ class AggregateController(wsgi.Controller):
|
||||||
except exception.InvalidAggregateAction as e:
|
except exception.InvalidAggregateAction as e:
|
||||||
raise exc.HTTPBadRequest(explanation=e.format_message())
|
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 = {}
|
_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
|
# NOTE(danms): The original API specified non-TZ-aware timestamps
|
||||||
if isinstance(value, datetime.datetime):
|
if isinstance(value, datetime.datetime):
|
||||||
value = value.replace(tzinfo=None)
|
value = value.replace(tzinfo=None)
|
||||||
_aggregate[key] = value
|
_aggregate[key] = value
|
||||||
return {"aggregate": _aggregate}
|
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
|
keys = aggregate.obj_fields
|
||||||
# NOTE(rlrossit): Within the compute API, metadata will always be
|
# NOTE(rlrossit): Within the compute API, metadata will always be
|
||||||
# set on the aggregate object (at a minimum to {}). Because of this,
|
# 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
|
# case it is only ['availability_zone']) without worrying about
|
||||||
# lazy-loading an unset variable
|
# lazy-loading an unset variable
|
||||||
for key in keys:
|
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)
|
if ((aggregate.obj_attr_is_set(key)
|
||||||
or key in aggregate.obj_extra_fields) and
|
or key in aggregate.obj_extra_fields) and
|
||||||
key != 'uuid'):
|
(show_uuid or key != 'uuid')):
|
||||||
yield key, getattr(aggregate, key)
|
yield key, getattr(aggregate, key)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -445,3 +445,11 @@ user documentation.
|
||||||
`CONF.api.max_limit` to encourage the adoption of this new microversion,
|
`CONF.api.max_limit` to encourage the adoption of this new microversion,
|
||||||
and circumvent the existing possibility DoS-like usage requests on systems
|
and circumvent the existing possibility DoS-like usage requests on systems
|
||||||
with thousands of instances.
|
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.
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"add_host": {
|
||||||
|
"host": "%(host_name)s"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"set_metadata":
|
||||||
|
{
|
||||||
|
"metadata":
|
||||||
|
{
|
||||||
|
"key": "value"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"aggregate":
|
||||||
|
{
|
||||||
|
"name": "name",
|
||||||
|
"availability_zone": "nova"
|
||||||
|
}
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"remove_host": {
|
||||||
|
"host": "%(host_name)s"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"aggregate":
|
||||||
|
{
|
||||||
|
"name": "newname",
|
||||||
|
"availability_zone": "nova2"
|
||||||
|
}
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,12 +13,18 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from oslo_serialization import jsonutils
|
||||||
|
|
||||||
from nova.tests.functional.api_sample_tests import api_sample_base
|
from nova.tests.functional.api_sample_tests import api_sample_base
|
||||||
|
|
||||||
|
|
||||||
class AggregatesSampleJsonTest(api_sample_base.ApiSampleTestBaseV21):
|
class AggregatesSampleJsonTest(api_sample_base.ApiSampleTestBaseV21):
|
||||||
ADMIN_API = True
|
ADMIN_API = True
|
||||||
sample_dir = "os-aggregates"
|
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):
|
def _test_aggregate_create(self):
|
||||||
subs = {
|
subs = {
|
||||||
|
@ -37,6 +43,7 @@ class AggregatesSampleJsonTest(api_sample_base.ApiSampleTestBaseV21):
|
||||||
}
|
}
|
||||||
response = self._do_post('os-aggregates/%s/action' % aggregate_id,
|
response = self._do_post('os-aggregates/%s/action' % aggregate_id,
|
||||||
'aggregate-add-host-post-req', subs)
|
'aggregate-add-host-post-req', subs)
|
||||||
|
subs.update(self.extra_subs)
|
||||||
self._verify_response('aggregates-add-host-post-resp', subs,
|
self._verify_response('aggregates-add-host-post-resp', subs,
|
||||||
response, 200)
|
response, 200)
|
||||||
|
|
||||||
|
@ -49,14 +56,15 @@ class AggregatesSampleJsonTest(api_sample_base.ApiSampleTestBaseV21):
|
||||||
def test_aggregate_get(self):
|
def test_aggregate_get(self):
|
||||||
agg_id = self._test_aggregate_create()
|
agg_id = self._test_aggregate_create()
|
||||||
response = self._do_get('os-aggregates/%s' % agg_id)
|
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):
|
def test_add_metadata(self):
|
||||||
agg_id = self._test_aggregate_create()
|
agg_id = self._test_aggregate_create()
|
||||||
response = self._do_post('os-aggregates/%s/action' % agg_id,
|
response = self._do_post('os-aggregates/%s/action' % agg_id,
|
||||||
'aggregate-metadata-post-req',
|
'aggregate-metadata-post-req',
|
||||||
{'action': 'set_metadata'})
|
{'action': 'set_metadata'})
|
||||||
self._verify_response('aggregates-metadata-post-resp', {},
|
self._verify_response('aggregates-metadata-post-resp', self.extra_subs,
|
||||||
response, 200)
|
response, 200)
|
||||||
|
|
||||||
def test_add_host(self):
|
def test_add_host(self):
|
||||||
|
@ -70,6 +78,7 @@ class AggregatesSampleJsonTest(api_sample_base.ApiSampleTestBaseV21):
|
||||||
}
|
}
|
||||||
response = self._do_post('os-aggregates/1/action',
|
response = self._do_post('os-aggregates/1/action',
|
||||||
'aggregate-remove-host-post-req', subs)
|
'aggregate-remove-host-post-req', subs)
|
||||||
|
subs.update(self.extra_subs)
|
||||||
self._verify_response('aggregates-remove-host-post-resp',
|
self._verify_response('aggregates-remove-host-post-resp',
|
||||||
subs, response, 200)
|
subs, response, 200)
|
||||||
|
|
||||||
|
@ -78,4 +87,33 @@ class AggregatesSampleJsonTest(api_sample_base.ApiSampleTestBaseV21):
|
||||||
response = self._do_put('os-aggregates/%s' % aggregate_id,
|
response = self._do_put('os-aggregates/%s' % aggregate_id,
|
||||||
'aggregate-update-post-req', {})
|
'aggregate-update-post-req', {})
|
||||||
self._verify_response('aggregate-update-post-resp',
|
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<id>\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)
|
||||||
|
|
|
@ -89,6 +89,9 @@ class _IntegratedTestBase(test.TestCase):
|
||||||
else:
|
else:
|
||||||
self.api = self.api_fixture.api
|
self.api = self.api_fixture.api
|
||||||
|
|
||||||
|
if hasattr(self, 'microversion'):
|
||||||
|
self.api.microversion = self.microversion
|
||||||
|
|
||||||
self.useFixture(cast_as_call.CastAsCall(self.stubs))
|
self.useFixture(cast_as_call.CastAsCall(self.stubs))
|
||||||
|
|
||||||
self.addCleanup(nova.tests.unit.image.fake.FakeImageService_reset)
|
self.addCleanup(nova.tests.unit.image.fake.FakeImageService_reset)
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
import mock
|
import mock
|
||||||
from webob import exc
|
from webob import exc
|
||||||
|
|
||||||
|
from nova.api.openstack import api_version_request
|
||||||
from nova.api.openstack.compute import aggregates as aggregates_v21
|
from nova.api.openstack.compute import aggregates as aggregates_v21
|
||||||
from nova.compute import api as compute_api
|
from nova.compute import api as compute_api
|
||||||
from nova import context
|
from nova import context
|
||||||
|
@ -743,11 +744,23 @@ class AggregateTestCaseV21(test.NoDBTestCase):
|
||||||
'metadata': {'foo': 'bar', 'availability_zone': 'nova'},
|
'metadata': {'foo': 'bar', 'availability_zone': 'nova'},
|
||||||
'hosts': ['host1', 'host2']}
|
'hosts': ['host1', 'host2']}
|
||||||
agg_obj = _make_agg_obj(agg)
|
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
|
# _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
|
# top-level dict, so we need to put availability_zone at the top also
|
||||||
agg['availability_zone'] = 'nova'
|
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']
|
del agg['uuid']
|
||||||
self.assertEqual(agg, marshalled_agg['aggregate'])
|
self.assertEqual(agg, marshalled_agg['aggregate'])
|
||||||
|
|
||||||
|
|
|
@ -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.
|
Loading…
Reference in New Issue