[placement] Symmetric GET and PUT /allocations/{consumer_uuid}

In a new microversion, 1.12, include project_id and user_id in the
output of GET /allocations/{consumer_uuid} and add JSON schema
to enable PUT to /allocations/{consumer_uuid} using the same dict-based
format for request body that is used in the GET response. In later
commits a similar format will be used in POST /allocations. This
symmetry is general good form and also will make client code a little
easier.

Since GET /allocation_candiates includes objects which are capable
of being PUT to /allocations/{consumer_uuid}, its response body has
been updated as well, to change the 'allocation_requests' object
to use the dict-based format.

Internally to handlers/allocation.py the same method (_set_allocations)
is used for every microversion. Any previous data structure is
transformed into the dict-ish form. This means that pre-existing tests
(like allocation-bad-class.yaml) continue to exercise the problems it
was made for, but needs to be pinned to an older microversion, rather than
being latest.

Info about these changes is added to placement-api-ref,
rest_api_version_history and a reno.

Change-Id: I49f5680c15413bce27f2abba68b699f3ea95dcdc
Implements: bp symmetric-allocations
Closes-Bug: #1708204
This commit is contained in:
Chris Dent 2017-10-16 21:27:40 +01:00
parent 9633ebdf37
commit 808323e0c5
19 changed files with 542 additions and 33 deletions

View File

@ -82,8 +82,64 @@ ALLOCATION_SCHEMA_V1_8['properties']['user_id'] = {'type': 'string',
'maxLength': 255}
ALLOCATION_SCHEMA_V1_8['required'].extend(['project_id', 'user_id'])
# Update the allocation schema to achieve symmetry with the representation
# used when GET /allocations/{consumer_uuid} is called.
# NOTE(cdent): Explicit duplication here for sake of comprehensibility.
ALLOCATION_SCHEMA_V1_12 = {
"type": "object",
"properties": {
"allocations": {
"type": "object",
"minProperties": 1,
# resource provider id
"patternProperties": {
"^[0-9a-fA-F-]{36}$": {
"type": "object",
"properties": {
# generation is optional
"generation": {
"type": "integer",
},
"resources": {
"type": "object",
"minProperties": 1,
# resource class
"patternProperties": {
"^[0-9A-Z_]+$": {
"type": "integer",
"minimum": 1,
}
},
"additionalProperties": False
}
},
"required": ["resources"],
"additionalProperties": False
}
},
"additionalProperties": False
},
"project_id": {
"type": "string",
"minLength": 1,
"maxLength": 255
},
"user_id": {
"type": "string",
"minLength": 1,
"maxLength": 255
}
},
"required": [
"allocations",
"project_id",
"user_id"
]
}
def _allocations_dict(allocations, key_fetcher, resource_provider=None):
def _allocations_dict(allocations, key_fetcher, resource_provider=None,
want_version=None):
"""Turn allocations into a dict of resources keyed by key_fetcher."""
allocation_data = collections.defaultdict(dict)
@ -102,10 +158,17 @@ def _allocations_dict(allocations, key_fetcher, resource_provider=None):
result = {'allocations': allocation_data}
if resource_provider:
result['resource_provider_generation'] = resource_provider.generation
else:
if allocations and want_version and want_version.matches((1, 12)):
# We're looking at a list of allocations by consumer id so
# project and user are consistent across the list
result['project_id'] = allocations[0].project_id
result['user_id'] = allocations[0].user_id
return result
def _serialize_allocations_for_consumer(allocations):
def _serialize_allocations_for_consumer(allocations, want_version=None):
"""Turn a list of allocations into a dict by resource provider uuid.
{
@ -124,11 +187,15 @@ def _serialize_allocations_for_consumer(allocations):
'VCPU': 3
}
}
}
},
# project_id and user_id are added with microverion 1.12
'project_id': PROJECT_ID,
'user_id': USER_ID
}
"""
return _allocations_dict(allocations,
lambda x: x.resource_provider.uuid)
lambda x: x.resource_provider.uuid,
want_version=want_version)
def _serialize_allocations_for_resource_provider(allocations,
@ -161,6 +228,7 @@ def list_for_consumer(req):
"""List allocations associated with a consumer."""
context = req.environ['placement.context']
consumer_id = util.wsgi_path_item(req.environ, 'consumer_uuid')
want_version = req.environ[microversion.MICROVERSION_ENVIRON]
# NOTE(cdent): There is no way for a 404 to be returned here,
# only an empty result. We do not have a way to validate a
@ -169,7 +237,7 @@ def list_for_consumer(req):
context, consumer_id)
allocations_json = jsonutils.dumps(
_serialize_allocations_for_consumer(allocations))
_serialize_allocations_for_consumer(allocations, want_version))
req.response.status = 200
req.response.body = encodeutils.to_utf8(allocations_json)
@ -215,12 +283,22 @@ def _set_allocations(req, schema):
data = util.extract_json(req.body, schema)
allocation_data = data['allocations']
# Normalize allocation data to dict.
want_version = req.environ[microversion.MICROVERSION_ENVIRON]
if not want_version.matches((1, 12)):
allocations_dict = {}
# Allocation are list-ish, transform to dict-ish
for allocation in allocation_data:
resource_provider_uuid = allocation['resource_provider']['uuid']
allocations_dict[resource_provider_uuid] = {
'resources': allocation['resources']
}
allocation_data = allocations_dict
# If the body includes an allocation for a resource provider
# that does not exist, raise a 400.
allocation_objects = []
for allocation in allocation_data:
resource_provider_uuid = allocation['resource_provider']['uuid']
for resource_provider_uuid, allocation in allocation_data.items():
try:
resource_provider = rp_obj.ResourceProvider.get_by_uuid(
context, resource_provider_uuid)
@ -276,12 +354,19 @@ def set_allocations(req):
@wsgi_wrapper.PlacementWsgify # noqa
@microversion.version_handler('1.8')
@microversion.version_handler('1.8', '1.11')
@util.require_content('application/json')
def set_allocations(req):
return _set_allocations(req, ALLOCATION_SCHEMA_V1_8)
@wsgi_wrapper.PlacementWsgify # noqa
@microversion.version_handler('1.12')
@util.require_content('application/json')
def set_allocations(req):
return _set_allocations(req, ALLOCATION_SCHEMA_V1_12)
@wsgi_wrapper.PlacementWsgify
def delete_allocations(req):
context = req.environ['placement.context']

View File

@ -45,7 +45,47 @@ _GET_SCHEMA_1_10 = {
}
def _transform_allocation_requests(alloc_reqs):
def _transform_allocation_requests_dict(alloc_reqs):
"""Turn supplied list of AllocationRequest objects into a list of
allocations dicts keyed by resource provider uuid of resources involved
in the allocation request. The returned results are intended to be used
as the body of a PUT /allocations/{consumer_uuid} HTTP request at
micoversion 1.12 (and beyond). The JSON objects look like the following:
[
{
"allocations": {
$rp_uuid1: {
"resources": {
"MEMORY_MB": 512
...
}
},
$rp_uuid2: {
"resources": {
"DISK_GB": 1024
...
}
}
},
},
...
]
"""
results = []
for ar in alloc_reqs:
# A default dict of {$rp_uuid: "resources": {})
rp_resources = collections.defaultdict(lambda: dict(resources={}))
for rr in ar.resource_requests:
res_dict = rp_resources[rr.resource_provider.uuid]['resources']
res_dict[rr.resource_class] = rr.amount
results.append(dict(allocations=rp_resources))
return results
def _transform_allocation_requests_list(alloc_reqs):
"""Turn supplied list of AllocationRequest objects into a list of dicts of
resources involved in the allocation request. The returned results is
intended to be able to be used as the body of a PUT
@ -132,7 +172,7 @@ def _transform_provider_summaries(p_sums):
}
def _transform_allocation_candidates(alloc_cands):
def _transform_allocation_candidates(alloc_cands, want_version):
"""Turn supplied AllocationCandidates object into a dict containing
allocation requests and provider summaries.
@ -141,7 +181,12 @@ def _transform_allocation_candidates(alloc_cands):
'provider_summaries': <PROVIDER_SUMMARIES>,
}
"""
a_reqs = _transform_allocation_requests(alloc_cands.allocation_requests)
if want_version.matches((1, 12)):
a_reqs = _transform_allocation_requests_dict(
alloc_cands.allocation_requests)
else:
a_reqs = _transform_allocation_requests_list(
alloc_cands.allocation_requests)
p_sums = _transform_provider_summaries(alloc_cands.provider_summaries)
return {
'allocation_requests': a_reqs,
@ -160,6 +205,7 @@ def list_allocation_candidates(req):
a collection of allocation requests and provider summaries
"""
context = req.environ['placement.context']
want_version = req.environ[microversion.MICROVERSION_ENVIRON]
schema = _GET_SCHEMA_1_10
util.validate_query_params(req, schema)
@ -173,7 +219,7 @@ def list_allocation_candidates(req):
{'error': exc})
response = req.response
trx_cands = _transform_allocation_candidates(cands)
trx_cands = _transform_allocation_candidates(cands, want_version)
json_data = jsonutils.dumps(trx_cands)
response.body = encodeutils.to_utf8(json_data)
response.content_type = 'application/json'

View File

@ -49,6 +49,9 @@ VERSIONS = [
'1.9', # Adds GET /usages
'1.10', # Adds GET /allocation_candidates resource endpoint
'1.11', # Adds 'allocations' link to the GET /resource_providers response
'1.12', # Add project_id and user_id to GET /allocations/{consumer_uuid}
# and PUT to /allocations/{consumer_uuid} in the same dict form
# as GET
]

View File

@ -159,3 +159,16 @@ for resources.
The ``/resource_providers/{rp_uuid}/allocations`` endpoint has been available
since version 1.0, but was not listed in the ``links`` section of the
``GET /resource_providers`` response. The link is included as of version 1.11.
1.12 PUT dict format to /allocations/{consumer_uuid}
----------------------------------------------------
In version 1.12 the request body of a ``PUT /allocations/{consumer_uuid}``
is expected to have an `object` for the ``allocations`` property, not as
`array` as with earlier microversions. This puts the request body more in
alignment with the structure of the ``GET /allocations/{consumer_uuid}``
response body. Because the `PUT` request requires `user_id` and
`project_id` in the request body, these fields are added to the `GET`
response. In addition, the response body for ``GET /allocation_candidates``
is updated so the allocations in the ``alocation_requests`` object work
with the new `PUT` format.

View File

@ -148,6 +148,7 @@ class AllocationFixture(APIFixture):
# Create some VCPU inventory and allocations.
consumer_id = uuidutils.generate_uuid()
os.environ['CONSUMER_ID'] = consumer_id
inventory = rp_obj.Inventory(
self.context, resource_provider=rp,
resource_class='VCPU', total=10,

View File

@ -7,7 +7,9 @@ defaults:
x-auth-token: admin
accept: application/json
content-type: application/json
openstack-api-version: placement latest
# Using <= 1.11 allows the PUT /allocations/{uuid} below
# to work with the older request form.
openstack-api-version: placement 1.11
tests:

View File

@ -83,3 +83,31 @@ tests:
# storage show correct capacity and usage
$.provider_summaries["$ENVIRON['SS_UUID']"].resources[DISK_GB].capacity: 1900 # 1.0 * 2000 - 100G
$.provider_summaries["$ENVIRON['SS_UUID']"].resources[DISK_GB].used: 0
# Verify the 1.12 format of the allocation_requests sub object which
# changes from a list-list to dict-ish format.
- name: get allocation candidates 1.12 dictish
GET: /allocation_candidates?resources=VCPU:1,MEMORY_MB:1024,DISK_GB:100
request_headers:
openstack-api-version: placement 1.12
response_json_paths:
# There are 3 providers involved. 2 compute nodes, 1 shared storage
# provider
$.provider_summaries.`len`: 3
# However, there are only 2 allocation requests, one for each compute
# node that provides the VCPU/MEMORY_MB and DISK_GB provided by the
# shared storage provider
$.allocation_requests.`len`: 2
# Verify that compute node #1 only has VCPU and MEMORY_MB listed in the
# resource requests. This validates the entire resources key.
$.allocation_requests..allocations["$ENVIRON['CN1_UUID']"].resources:
VCPU: 1
MEMORY_MB: 1024
# Verify that compute node #2 only has VCPU and MEMORY_MB listed in the
# resource requests
$.allocation_requests..allocations["$ENVIRON['CN2_UUID']"].resources:
VCPU: 1
MEMORY_MB: 1024
# Verify that shared storage provider only has DISK_GB listed in the
# resource requests, but is listed twice
$.allocation_requests..allocations["$ENVIRON['SS_UUID']"].resources[DISK_GB]: [100, 100]

View File

@ -0,0 +1,115 @@
fixtures:
- APIFixture
defaults:
request_headers:
x-auth-token: admin
accept: application/json
content-type: application/json
openstack-api-version: placement 1.12
tests:
- name: put an allocation listish
PUT: /allocations/a0b15655-273a-4b3d-9792-2e579b7d5ad9
data:
allocations:
- resource_provider:
uuid: $ENVIRON['RP_UUID']
resources:
DISK_GB: 10
project_id: $ENVIRON['PROJECT_ID']
user_id: $ENVIRON['USER_ID']
status: 400
response_strings:
- JSON does not validate
- name: put resource provider not uuid
PUT: /allocations/a0b15655-273a-4b3d-9792-2e579b7d5ad9
data:
allocations:
nice_house_friend:
resources:
VCPU: 1
DISK_GB: 20
project_id: 42a32c07-3eeb-4401-9373-68a8cdca6784
user_id: 66cb2f29-c86d-47c3-8af5-69ae7b778c70
status: 400
response_strings:
- JSON does not validate
- does not match any of the regexes
- name: put resource class not valid
PUT: /allocations/a0b15655-273a-4b3d-9792-2e579b7d5ad9
data:
allocations:
$ENVIRON['RP_UUID']:
resources:
vcpu: 1
DISK_GB: 20
project_id: 42a32c07-3eeb-4401-9373-68a8cdca6784
user_id: 66cb2f29-c86d-47c3-8af5-69ae7b778c70
status: 400
response_strings:
- JSON does not validate
- does not match any of the regexes
- name: put empty allocations
PUT: /allocations/a0b15655-273a-4b3d-9792-2e579b7d5ad9
data:
allocations: {}
project_id: 42a32c07-3eeb-4401-9373-68a8cdca6784
user_id: 66cb2f29-c86d-47c3-8af5-69ae7b778c70
status: 400
response_strings:
- JSON does not validate
- does not have enough properties
- name: create the resource provider
POST: /resource_providers
request_headers:
content-type: application/json
data:
name: $ENVIRON['RP_NAME']
uuid: $ENVIRON['RP_UUID']
status: 201
- name: set some inventory
PUT: /resource_providers/$ENVIRON['RP_UUID']/inventories
request_headers:
content-type: application/json
data:
resource_provider_generation: 0
inventories:
DISK_GB:
total: 2048
min_unit: 10
max_unit: 1024
VCPU:
total: 96
status: 200
- name: put an allocation dictish
PUT: /allocations/a0b15655-273a-4b3d-9792-2e579b7d5ad9
data:
allocations:
$ENVIRON['RP_UUID']:
resources:
VCPU: 1
DISK_GB: 20
project_id: 42a32c07-3eeb-4401-9373-68a8cdca6784
user_id: 66cb2f29-c86d-47c3-8af5-69ae7b778c70
status: 204
- name: get that allocation
GET: $LAST_URL
- name: put that same allocation back
PUT: $LAST_URL
data:
# there's a generation in allocations, ignored
allocations: $RESPONSE['$.allocations']
# project_id and user_id not in the get response so we add it
project_id: 42a32c07-3eeb-4401-9373-68a8cdca6784
user_id: 66cb2f29-c86d-47c3-8af5-69ae7b778c70
status: 204

View File

@ -11,7 +11,9 @@ defaults:
x-auth-token: admin
accept: application/json
content-type: application/json
openstack-api-version: placement latest
# Default to <= 1.11 so the PUT /allocations in here that use the
# older list-ish format continue to work.
openstack-api-version: placement 1.11
tests:
@ -61,3 +63,35 @@ tests:
status: 400
response_strings:
- does not have enough properties
# The next two tests confirm that the bug identified by
# this file's name is not present in the PUT /allocations/{consumer_uuid}
# format added by microversion 1.12.
- name: put a successful dictish allocation
PUT: /allocations/c9f0186b-64f8-44fb-b6c9-83008d8d6940
request_headers:
openstack-api-version: placement 1.12
data:
allocations:
$HISTORY['get resource provider'].$RESPONSE['$.uuid']:
resources:
VCPU: 1
MEMORY_MB: 1
project_id: 42a32c07-3eeb-4401-9373-68a8cdca6784
user_id: 66cb2f29-c86d-47c3-8af5-69ae7b778c70
status: 204
- name: fail with empty resources dictish
PUT: /allocations/c9f0186b-64f8-44fb-b6c9-83008d8d6940
request_headers:
openstack-api-version: placement 1.12
data:
allocations:
$HISTORY['get resource provider'].$RESPONSE['$.uuid']:
resources: {}
project_id: 42a32c07-3eeb-4401-9373-68a8cdca6784
user_id: 66cb2f29-c86d-47c3-8af5-69ae7b778c70
status: 400
response_strings:
- does not have enough properties

View File

@ -39,13 +39,13 @@ tests:
response_json_paths:
$.errors[0].title: Not Acceptable
- name: latest microversion is 1.11
- name: latest microversion is 1.12
GET: /
request_headers:
openstack-api-version: placement latest
response_headers:
vary: /OpenStack-API-Version/
openstack-api-version: placement 1.11
openstack-api-version: placement 1.12
- name: other accept header bad version
GET: /

View File

@ -9,7 +9,9 @@ defaults:
x-auth-token: admin
accept: application/json
content-type: application/json
openstack-api-version: placement latest
# We need version 1.11 as the PUT /allocations below is
# using the < 1.12 data format.
openstack-api-version: placement 1.11
tests:

View File

@ -68,3 +68,22 @@ tests:
response_json_paths:
$.usages.DISK_GB: 20
$.usages.VCPU: 1
- name: get allocations without project and user
GET: /allocations/$ENVIRON['CONSUMER_ID']
request_headers:
openstack-api-version: placement 1.11
accept: application/json
response_json_paths:
# only one key in the top level object
$.`len`: 1
- name: get allocations with project and user
GET: /allocations/$ENVIRON['CONSUMER_ID']
request_headers:
openstack-api-version: placement 1.12
accept: application/json
response_json_paths:
$.project_id: $ENVIRON['PROJECT_ID']
$.user_id: $ENVIRON['USER_ID']
$.`len`: 3

View File

@ -31,8 +31,26 @@ Request
- resources: resources_query_required
Response
--------
Response (microversions 1.12 - )
--------------------------------
.. rest_parameters:: parameters.yaml
- allocation_requests: allocation_requests
- provider_summaries: provider_summaries
- allocations: allocations_by_resource_provider
- resources: resources
- capacity: capacity
- used: used
Response Example (microversions 1.12 - )
----------------------------------------
.. literalinclude:: get-allocation_candidates-1.12.json
:language: javascript
Response (microversions 1.10 - 1.11)
------------------------------------
.. rest_parameters:: parameters.yaml
@ -45,8 +63,8 @@ Response
- capacity: capacity
- used: used
Response Example
----------------
Response Example (microversions 1.10 - 1.11)
--------------------------------------------
.. literalinclude:: get-allocation_candidates.json
:language: javascript

View File

@ -32,6 +32,8 @@ Response
- allocations: allocations_by_resource_provider
- generation: resource_provider_generation
- resources: resources
- project_id: project_id_body_1_12
- user_id: user_id_body_1_12
Response Example
----------------
@ -57,8 +59,26 @@ Error response codes: badRequest(400), itemNotFound(404), conflict(409)
resource providers for any specified resource classes or inventories
are updated by another thread while attempting the operation.
Request
-------
Request (microversions 1.12 - )
-------------------------------
.. rest_parameters:: parameters.yaml
- consumer_uuid: consumer_uuid
- allocations: allocations_dict
- resources: resources
- project_id: project_id_body
- user_id: user_id_body
- generation: resource_provider_generation_optional
Request example (microversions 1.12 - )
---------------------------------------
.. literalinclude:: update-allocations-request-1.12.json
:language: javascript
Request (microversions 1.0 - 1.11)
----------------------------------
.. rest_parameters:: parameters.yaml
@ -67,11 +87,11 @@ Request
- resources: resources
- resource_provider: resource_provider_object
- uuid: resource_provider_uuid
- project_id: project_id_body
- user_id: user_id_body
- project_id: project_id_body_1_8
- user_id: user_id_body_1_8
Request example
---------------
Request example (microversions 1.0 - 1.11)
------------------------------------------
.. literalinclude:: update-allocations-request.json
:language: javascript

View File

@ -0,0 +1,68 @@
{
"allocation_requests": [
{
"allocations": {
"a99bad54-a275-4c4f-a8a3-ac00d57e5c64": {
"resources": {
"DISK_GB": 100
}
},
"35791f28-fb45-4717-9ea9-435b3ef7c3b3": {
"resources": {
"VCPU": 1,
"MEMORY_MB": 1024
}
}
}
},
{
"allocations": {
"a99bad54-a275-4c4f-a8a3-ac00d57e5c64": {
"resources": {
"DISK_GB": 100
}
},
"915ef8ed-9b91-4e38-8802-2e4224ad54cd": {
"resources": {
"VCPU": 1,
"MEMORY_MB": 1024
}
}
}
}
],
"provider_summaries": {
"a99bad54-a275-4c4f-a8a3-ac00d57e5c64": {
"resources": {
"DISK_GB": {
"used": 0,
"capacity": 1900
}
}
},
"915ef8ed-9b91-4e38-8802-2e4224ad54cd": {
"resources": {
"VCPU": {
"used": 0,
"capacity": 384
},
"MEMORY_MB": {
"used": 0,
"capacity": 196608
}
}
},
"35791f28-fb45-4717-9ea9-435b3ef7c3b3": {
"resources": {
"VCPU": {
"used": 0,
"capacity": 384
},
"MEMORY_MB": {
"used": 0,
"capacity": 196608
}
}
}
}
}

View File

@ -13,5 +13,7 @@
"VCPU": 2
}
}
}
},
"project_id": "7e67cbf7-7c38-4a32-b85b-0739c690991a",
"user_id": "067f691e-725a-451a-83e2-5c3d13e1dffc"
}

View File

@ -143,6 +143,13 @@ allocations_by_resource_provider:
required: true
description: >
A dictionary of allocations keyed by resource provider uuid.
allocations_dict:
type: object
in: body
required: true
min_version: 1.12
description: >
A dictionary of resource allocations keyed by resource provider uuid.
capacity:
type: integer
in: body
@ -173,9 +180,14 @@ min_unit: &min_unit
min_unit_opt:
<<: *min_unit
required: false
project_id_body:
project_id_body: &project_id_body
<<: *project_id
in: body
project_id_body_1_12:
<<: *project_id_body
min_version: 1.12
project_id_body_1_8:
<<: *project_id_body
min_version: 1.8
provider_summaries:
type: object
@ -214,13 +226,21 @@ resource_provider_allocations:
required: true
description: >
A dictionary of allocation records keyed by consumer uuid.
resource_provider_generation:
resource_provider_generation: &resource_provider_generation
type: integer
in: body
required: true
description: >
A consistent view marker that assists with the management of
concurrent resource provider updates.
resource_provider_generation_optional:
<<: *resource_provider_generation
required: false
description: >
A consistent view marker that assists with the management of
concurrent resource provider updates. The value is ignored;
it is present to preserve symmetry between read and
write representations.
resource_provider_links:
type: array
in: body
@ -301,10 +321,15 @@ used:
required: true
description: >
The amount of the resource that has been already allocated.
user_id_body:
user_id_body: &user_id_body
<<: *user_id
in: body
required: true
user_id_body_1_12:
<<: *user_id_body
min_version: 1.12
user_id_body_1_8:
<<: *user_id_body
min_version: 1.8
version_id:
type: string

View File

@ -0,0 +1,17 @@
{
"allocations": {
"4e061c03-611e-4caa-bf26-999dcff4284e": {
"resources": {
"DISK_GB": 20
}
},
"89873422-1373-46e5-b467-f0c5e6acf08f": {
"resources": {
"MEMORY_MB": 1024,
"VCPU": 1
}
}
},
"user_id": "66cb2f29-c86d-47c3-8af5-69ae7b778c70",
"project_id": "42a32c07-3eeb-4401-9373-68a8cdca6784"
}

View File

@ -0,0 +1,11 @@
---
features:
- |
The 1.12 version of the placement API changes handling of the `PUT
/allocations/{consumer_uuid}` request to use a dict-based structure for
the JSON request body to make it more aligned with the response body of
`GET /allocations/{consumer_uuid}`. Because `PUT` requires `user_id`
and `project_id` in the request body, these fields are added to the
`GET` response. In addition, the response body for
``GET /allocation_candidates`` is updated so the allocations in the
``alocation_requests`` object work with the new `PUT` format.