From 8caf4f514898ae1af2c3d0e1e477a5d5f3060c60 Mon Sep 17 00:00:00 2001 From: Chris Dent Date: Fri, 1 Sep 2017 15:16:22 +0100 Subject: [PATCH] [placement] POST /allocations to set allocations for >1 consumers This provides microversion 1.13 of the placement API, giving the ability to POST to /allocations to set (or clear) allocations for more than one consumer uuid. It builds on the recent work to support a dict-based JSON format when doing a PUT to /allocations/{consumer_uuid}. Being able to set allocations for multiple consumers in one request helps to address race conditions when cleaning up allocations during move operations in nova. Clearing allocations is done by setting the 'allocations' key for a specific consumer to an empty dict. Updates to placement-api-ref, rest version history and a reno are included. Change-Id: I239f33841bb9fcd92b406f979674ae8c5f8d57e3 Implements: bp post-allocations --- nova/api/openstack/placement/handler.py | 5 +- .../placement/handlers/allocation.py | 153 ++++++++-- nova/api/openstack/placement/microversion.py | 1 + .../placement/rest_api_version_history.rst | 6 + .../api/openstack/placement/fixtures.py | 5 + .../placement/gabbits/allocations-post.yaml | 288 ++++++++++++++++++ .../placement/gabbits/allocations.yaml | 6 +- .../placement/gabbits/microversion.yaml | 4 +- .../openstack/placement/test_microversion.py | 2 +- placement-api-ref/source/allocations.inc | 45 +++ .../source/manage-allocations-request.json | 31 ++ placement-api-ref/source/parameters.yaml | 14 +- .../post-allocations-427581fa41671820.yaml | 6 + 13 files changed, 532 insertions(+), 34 deletions(-) create mode 100644 nova/tests/functional/api/openstack/placement/gabbits/allocations-post.yaml create mode 100644 placement-api-ref/source/manage-allocations-request.json create mode 100644 releasenotes/notes/post-allocations-427581fa41671820.yaml diff --git a/nova/api/openstack/placement/handler.py b/nova/api/openstack/placement/handler.py index a54360ded556..38ed64c51310 100644 --- a/nova/api/openstack/placement/handler.py +++ b/nova/api/openstack/placement/handler.py @@ -100,9 +100,12 @@ ROUTE_DECLARATIONS = { '/resource_providers/{uuid}/allocations': { 'GET': allocation.list_for_resource_provider, }, + '/allocations': { + 'POST': allocation.set_allocations, + }, '/allocations/{consumer_uuid}': { 'GET': allocation.list_for_consumer, - 'PUT': allocation.set_allocations, + 'PUT': allocation.set_allocations_for_consumer, 'DELETE': allocation.delete_allocations, }, '/allocation_candidates': { diff --git a/nova/api/openstack/placement/handlers/allocation.py b/nova/api/openstack/placement/handlers/allocation.py index 1504ff415597..cb5a7c528b72 100644 --- a/nova/api/openstack/placement/handlers/allocation.py +++ b/nova/api/openstack/placement/handlers/allocation.py @@ -138,6 +138,24 @@ ALLOCATION_SCHEMA_V1_12 = { } +# POST to /allocations, added in microversion 1.13, uses the +# POST_ALLOCATIONS_V1_13 schema to allow multiple allocations +# from multiple consumers in one request. It is a dict, keyed by +# consumer uuid, using the form of PUT allocations from microversion +# 1.12. In POST the allocations can be empty, so DELETABLE_ALLOCATIONS +# modifies ALLOCATION_SCHEMA_V1_12 accordingly. +DELETABLE_ALLOCATIONS = copy.deepcopy(ALLOCATION_SCHEMA_V1_12) +DELETABLE_ALLOCATIONS['properties']['allocations']['minProperties'] = 0 +POST_ALLOCATIONS_V1_13 = { + "type": "object", + "minProperties": 1, + "additionalProperties": False, + "patternProperties": { + "^[0-9a-fA-F-]{36}$": DELETABLE_ALLOCATIONS + } +} + + def _allocations_dict(allocations, key_fetcher, resource_provider=None, want_version=None): """Turn allocations into a dict of resources keyed by key_fetcher.""" @@ -277,7 +295,42 @@ def list_for_resource_provider(req): return req.response -def _set_allocations(req, schema): +def _new_allocations(context, resource_provider_uuid, consumer_uuid, + resources, project_id, user_id): + """Create new allocation objects for a set of resources + + Returns a list of Allocation objects. + + :param context: The placement context. + :param resource_provider_uuid: The uuid of the resource provider that + has the resources. + :param consumer_uuid: The uuid of the consumer of the resources. + :param resources: A dict of resource classes and values. + :param project_id: The project consuming the resources. + :param user_id: The user consuming the resources. + """ + allocations = [] + try: + resource_provider = rp_obj.ResourceProvider.get_by_uuid( + context, resource_provider_uuid) + except exception.NotFound: + raise webob.exc.HTTPBadRequest( + _("Allocation for resource provider '%(rp_uuid)s' " + "that does not exist.") % + {'rp_uuid': resource_provider_uuid}) + for resource_class in resources: + allocation = rp_obj.Allocation( + resource_provider=resource_provider, + consumer_id=consumer_uuid, + resource_class=resource_class, + project_id=project_id, + user_id=user_id, + used=resources[resource_class]) + allocations.append(allocation) + return allocations + + +def _set_allocations_for_consumer(req, schema): context = req.environ['placement.context'] consumer_uuid = util.wsgi_path_item(req.environ, 'consumer_uuid') data = util.extract_json(req.body, schema) @@ -299,25 +352,13 @@ def _set_allocations(req, schema): # that does not exist, raise a 400. allocation_objects = [] for resource_provider_uuid, allocation in allocation_data.items(): - try: - resource_provider = rp_obj.ResourceProvider.get_by_uuid( - context, resource_provider_uuid) - except exception.NotFound: - raise webob.exc.HTTPBadRequest( - _("Allocation for resource provider '%(rp_uuid)s' " - "that does not exist.") % - {'rp_uuid': resource_provider_uuid}) - - resources = allocation['resources'] - for resource_class in resources: - allocation = rp_obj.Allocation( - resource_provider=resource_provider, - consumer_id=consumer_uuid, - resource_class=resource_class, - project_id=data.get('project_id'), - user_id=data.get('user_id'), - used=resources[resource_class]) - allocation_objects.append(allocation) + new_allocations = _new_allocations(context, + resource_provider_uuid, + consumer_uuid, + allocation['resources'], + data.get('project_id'), + data.get('user_id')) + allocation_objects.extend(new_allocations) allocations = rp_obj.AllocationList( context, objects=allocation_objects) @@ -349,22 +390,84 @@ def _set_allocations(req, schema): @wsgi_wrapper.PlacementWsgify @microversion.version_handler('1.0', '1.7') @util.require_content('application/json') -def set_allocations(req): - return _set_allocations(req, ALLOCATION_SCHEMA) +def set_allocations_for_consumer(req): + return _set_allocations_for_consumer(req, ALLOCATION_SCHEMA) @wsgi_wrapper.PlacementWsgify # noqa @microversion.version_handler('1.8', '1.11') @util.require_content('application/json') -def set_allocations(req): - return _set_allocations(req, ALLOCATION_SCHEMA_V1_8) +def set_allocations_for_consumer(req): + return _set_allocations_for_consumer(req, ALLOCATION_SCHEMA_V1_8) @wsgi_wrapper.PlacementWsgify # noqa @microversion.version_handler('1.12') @util.require_content('application/json') +def set_allocations_for_consumer(req): + return _set_allocations_for_consumer(req, ALLOCATION_SCHEMA_V1_12) + + +@wsgi_wrapper.PlacementWsgify +@microversion.version_handler('1.13') +@util.require_content('application/json') def set_allocations(req): - return _set_allocations(req, ALLOCATION_SCHEMA_V1_12) + context = req.environ['placement.context'] + data = util.extract_json(req.body, POST_ALLOCATIONS_V1_13) + + # Create a sequence of allocation objects to be used in an + # AllocationList.create_all() call, which will mean all the changes + # happen within a single transaction and with resource provider + # generations check all in one go. + allocation_objects = [] + + for consumer_uuid in data: + project_id = data[consumer_uuid]['project_id'] + user_id = data[consumer_uuid]['user_id'] + allocations = data[consumer_uuid]['allocations'] + if allocations: + for resource_provider_uuid in allocations: + resources = allocations[resource_provider_uuid]['resources'] + new_allocations = _new_allocations(context, + resource_provider_uuid, + consumer_uuid, + resources, + project_id, + user_id) + allocation_objects.extend(new_allocations) + else: + # The allocations are empty, which means wipe them out. + # Internal to the allocation object this is signalled by a + # used value of 0. + allocations = rp_obj.AllocationList.get_all_by_consumer_id( + context, consumer_uuid) + for allocation in allocations: + allocation.used = 0 + allocation_objects.append(allocation) + + allocations = rp_obj.AllocationList( + context, objects=allocation_objects) + + try: + allocations.create_all() + LOG.debug("Successfully wrote allocations %s", allocations) + except exception.NotFound as exc: + raise webob.exc.HTTPBadRequest( + _("Unable to allocate inventory %(error)s") % {'error': exc}) + except exception.InvalidInventory as exc: + # InvalidInventory is a parent for several exceptions that + # indicate either that Inventory is not present, or that + # capacity limits have been exceeded. + raise webob.exc.HTTPConflict( + _('Unable to allocate inventory: %(error)s') % {'error': exc}) + except exception.ConcurrentUpdateDetected as exc: + raise webob.exc.HTTPConflict( + _('Inventory changed while attempting to allocate: %(error)s') % + {'error': exc}) + + req.response.status = 204 + req.response.content_type = None + return req.response @wsgi_wrapper.PlacementWsgify diff --git a/nova/api/openstack/placement/microversion.py b/nova/api/openstack/placement/microversion.py index 410aab3bbe5d..210acca4027f 100644 --- a/nova/api/openstack/placement/microversion.py +++ b/nova/api/openstack/placement/microversion.py @@ -52,6 +52,7 @@ VERSIONS = [ '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 + '1.13', # Adds POST /allocations to set allocations for multiple consumers ] diff --git a/nova/api/openstack/placement/rest_api_version_history.rst b/nova/api/openstack/placement/rest_api_version_history.rst index ef2c61c6f7d4..b4a2c1827267 100644 --- a/nova/api/openstack/placement/rest_api_version_history.rst +++ b/nova/api/openstack/placement/rest_api_version_history.rst @@ -172,3 +172,9 @@ response body. Because the `PUT` request requires `user_id` and 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. + +1.13 POST multiple allocations to /allocations +---------------------------------------------- + +Version 1.13 gives the ability to set or clear allocations for more than +one consumer uuid with a request to ``POST /allocations``. diff --git a/nova/tests/functional/api/openstack/placement/fixtures.py b/nova/tests/functional/api/openstack/placement/fixtures.py index cbd3518b1cf9..f419cd4f8d98 100644 --- a/nova/tests/functional/api/openstack/placement/fixtures.py +++ b/nova/tests/functional/api/openstack/placement/fixtures.py @@ -80,6 +80,11 @@ class APIFixture(fixture.GabbiFixture): os.environ['CUSTOM_RES_CLASS'] = 'CUSTOM_IRON_NFV' os.environ['PROJECT_ID'] = uuidutils.generate_uuid() os.environ['USER_ID'] = uuidutils.generate_uuid() + os.environ['PROJECT_ID_ALT'] = uuidutils.generate_uuid() + os.environ['USER_ID_ALT'] = uuidutils.generate_uuid() + os.environ['INSTANCE_UUID'] = uuidutils.generate_uuid() + os.environ['MIGRATION_UUID'] = uuidutils.generate_uuid() + os.environ['CONSUMER_UUID'] = uuidutils.generate_uuid() def stop_fixture(self): self.api_db_fixture.cleanup() diff --git a/nova/tests/functional/api/openstack/placement/gabbits/allocations-post.yaml b/nova/tests/functional/api/openstack/placement/gabbits/allocations-post.yaml new file mode 100644 index 000000000000..e1a7fdaf8732 --- /dev/null +++ b/nova/tests/functional/api/openstack/placement/gabbits/allocations-post.yaml @@ -0,0 +1,288 @@ +# Test that it possible to POST multiple allocations to /allocations to +# simultaneously make changes, including removing resources for a consumer if +# the allocations are empty. + +fixtures: + - APIFixture + +defaults: + request_headers: + x-auth-token: admin + accept: application/json + content-type: application/json + openstack-api-version: placement 1.13 + +tests: + +- name: create compute one + POST: /resource_providers + data: + name: compute01 + status: 201 + +- name: rp compute01 + desc: provide a reference for later reuse + GET: $LOCATION + +- name: create compute two + POST: /resource_providers + data: + name: compute02 + status: 201 + +- name: rp compute02 + desc: provide a reference for later reuse + GET: $LOCATION + +- name: create shared disk + POST: /resource_providers + data: + name: storage01 + status: 201 + +- name: rp storage01 + desc: provide a reference for later reuse + GET: $LOCATION + +- name: inventory compute01 + PUT: $HISTORY['rp compute01'].$RESPONSE['links[?rel = "inventories"].href'] + data: + resource_provider_generation: 0 + inventories: + VCPU: + total: 16 + MEMORY_MB: + total: 2048 + +- name: inventory compute02 + PUT: $HISTORY['rp compute02'].$RESPONSE['links[?rel = "inventories"].href'] + data: + resource_provider_generation: 0 + inventories: + VCPU: + total: 16 + MEMORY_MB: + total: 2048 + +- name: inventory storage01 + PUT: $HISTORY['rp storage01'].$RESPONSE['links[?rel = "inventories"].href'] + data: + resource_provider_generation: 0 + inventories: + DISK_GB: + total: 4096 + +- name: confirm only POST + GET: /allocations + status: 405 + response_headers: + allow: POST + +- name: 404 on older 1.12 microversion post + POST: /allocations + request_headers: + openstack-api-version: placement 1.12 + status: 404 + +- name: post allocations two consumers + POST: /allocations + data: + $ENVIRON['INSTANCE_UUID']: + allocations: + $HISTORY['rp compute02'].$RESPONSE['uuid']: + resources: + MEMORY_MB: 1024 + VCPU: 2 + $HISTORY['rp storage01'].$RESPONSE['uuid']: + resources: + DISK_GB: 5 + project_id: $ENVIRON['PROJECT_ID'] + user_id: $ENVIRON['USER_ID'] + $ENVIRON['MIGRATION_UUID']: + allocations: + $HISTORY['rp compute01'].$RESPONSE['uuid']: + resources: + MEMORY_MB: 1024 + VCPU: 2 + project_id: $ENVIRON['PROJECT_ID'] + user_id: $ENVIRON['USER_ID'] + status: 204 + +- name: confirm usages + GET: /usages?project_id=$ENVIRON['PROJECT_ID'] + response_json_paths: + $.usages.DISK_GB: 5 + $.usages.VCPU: 4 + $.usages.MEMORY_MB: 2048 + +- name: clear and set allocations + POST: /allocations + data: + $ENVIRON['INSTANCE_UUID']: + allocations: + $HISTORY['rp compute02'].$RESPONSE['uuid']: + resources: + MEMORY_MB: 1024 + VCPU: 2 + $HISTORY['rp storage01'].$RESPONSE['uuid']: + resources: + DISK_GB: 5 + project_id: $ENVIRON['PROJECT_ID'] + user_id: $ENVIRON['USER_ID'] + $ENVIRON['MIGRATION_UUID']: + allocations: {} + project_id: $ENVIRON['PROJECT_ID'] + user_id: $ENVIRON['USER_ID'] + status: 204 + +- name: confirm usages after clear + GET: /usages?project_id=$ENVIRON['PROJECT_ID'] + response_json_paths: + $.usages.DISK_GB: 5 + $.usages.VCPU: 2 + $.usages.MEMORY_MB: 1024 + +- name: post allocations two users + POST: /allocations + data: + $ENVIRON['INSTANCE_UUID']: + allocations: + $HISTORY['rp compute02'].$RESPONSE['uuid']: + resources: + MEMORY_MB: 1024 + VCPU: 2 + $HISTORY['rp storage01'].$RESPONSE['uuid']: + resources: + DISK_GB: 5 + project_id: $ENVIRON['PROJECT_ID'] + user_id: $ENVIRON['USER_ID'] + # We must use a fresh consumer id with the alternate project id info. + # A previously seen consumer id will be assumed to always have the same + # project and user. + $ENVIRON['CONSUMER_UUID']: + allocations: + $HISTORY['rp compute01'].$RESPONSE['uuid']: + resources: + MEMORY_MB: 1024 + VCPU: 2 + project_id: $ENVIRON['PROJECT_ID_ALT'] + user_id: $ENVIRON['USER_ID_ALT'] + status: 204 + +- name: confirm usages user a + GET: /usages?project_id=$ENVIRON['PROJECT_ID'] + response_json_paths: + $.usages.`len`: 3 + $.usages.DISK_GB: 5 + $.usages.VCPU: 2 + $.usages.MEMORY_MB: 1024 + +- name: confirm usages user b + GET: /usages?project_id=$ENVIRON['PROJECT_ID_ALT'] + response_json_paths: + $.usages.`len`: 2 + $.usages.VCPU: 2 + $.usages.MEMORY_MB: 1024 + +- name: fail allocations over capacity + POST: /allocations + data: + $ENVIRON['INSTANCE_UUID']: + allocations: + $HISTORY['rp compute02'].$RESPONSE['uuid']: + resources: + MEMORY_MB: 1024 + VCPU: 2 + $HISTORY['rp storage01'].$RESPONSE['uuid']: + resources: + DISK_GB: 5 + project_id: $ENVIRON['PROJECT_ID'] + user_id: $ENVIRON['USER_ID'] + $ENVIRON['CONSUMER_UUID']: + allocations: + $HISTORY['rp compute01'].$RESPONSE['uuid']: + resources: + MEMORY_MB: 2049 + VCPU: 2 + project_id: $ENVIRON['PROJECT_ID_ALT'] + user_id: $ENVIRON['USER_ID_ALT'] + status: 409 + response_strings: + - The requested amount would exceed the capacity + +- name: fail allocations deep schema violate + desc: no schema yet + POST: /allocations + data: + $ENVIRON['INSTANCE_UUID']: + allocations: + $HISTORY['rp compute02'].$RESPONSE['uuid']: + cow: moo + project_id: $ENVIRON['PROJECT_ID'] + user_id: $ENVIRON['USER_ID'] + status: 400 + +- name: fail allocations shallow schema violate + desc: no schema yet + POST: /allocations + data: + $ENVIRON['INSTANCE_UUID']: + cow: moo + status: 400 + +- name: fail resource provider not exist + POST: /allocations + data: + $ENVIRON['INSTANCE_UUID']: + allocations: + # this rp does not exist + 'c42def7b-498b-4442-9502-c7970b14bea4': + resources: + MEMORY_MB: 1024 + VCPU: 2 + $HISTORY['rp storage01'].$RESPONSE['uuid']: + resources: + DISK_GB: 5 + project_id: $ENVIRON['PROJECT_ID'] + user_id: $ENVIRON['USER_ID'] + status: 400 + response_strings: + - that does not exist + +- name: fail resource class not in inventory + POST: /allocations + data: + $ENVIRON['INSTANCE_UUID']: + allocations: + $HISTORY['rp compute02'].$RESPONSE['uuid']: + resources: + MEMORY_MB: 1024 + VCPU: 2 + PCI_DEVICE: 1 + $HISTORY['rp storage01'].$RESPONSE['uuid']: + resources: + DISK_GB: 5 + project_id: $ENVIRON['PROJECT_ID'] + user_id: $ENVIRON['USER_ID'] + status: 409 + response_strings: + - "Inventory for 'PCI_DEVICE' on" + +- name: fail resource class not exist + POST: /allocations + data: + $ENVIRON['INSTANCE_UUID']: + allocations: + $HISTORY['rp compute02'].$RESPONSE['uuid']: + resources: + MEMORY_MB: 1024 + VCPU: 2 + CUSTOM_PONY: 1 + $HISTORY['rp storage01'].$RESPONSE['uuid']: + resources: + DISK_GB: 5 + project_id: $ENVIRON['PROJECT_ID'] + user_id: $ENVIRON['USER_ID'] + status: 400 + response_strings: + - No such resource class CUSTOM_PONY diff --git a/nova/tests/functional/api/openstack/placement/gabbits/allocations.yaml b/nova/tests/functional/api/openstack/placement/gabbits/allocations.yaml index bf9b64833202..403e9bbf7187 100644 --- a/nova/tests/functional/api/openstack/placement/gabbits/allocations.yaml +++ b/nova/tests/functional/api/openstack/placement/gabbits/allocations.yaml @@ -14,11 +14,11 @@ defaults: tests: -- name: get allocations no consumer is 404 +- name: get allocations no consumer is 405 GET: /allocations - status: 404 + status: 405 response_json_paths: - $.errors[0].title: Not Found + $.errors[0].title: Method Not Allowed - name: get allocations is empty dict GET: /allocations/599ffd2d-526a-4b2e-8683-f13ad25f9958 diff --git a/nova/tests/functional/api/openstack/placement/gabbits/microversion.yaml b/nova/tests/functional/api/openstack/placement/gabbits/microversion.yaml index 6255f6833ccb..ccbdcbb8c9c6 100644 --- a/nova/tests/functional/api/openstack/placement/gabbits/microversion.yaml +++ b/nova/tests/functional/api/openstack/placement/gabbits/microversion.yaml @@ -39,13 +39,13 @@ tests: response_json_paths: $.errors[0].title: Not Acceptable -- name: latest microversion is 1.12 +- name: latest microversion is 1.13 GET: / request_headers: openstack-api-version: placement latest response_headers: vary: /OpenStack-API-Version/ - openstack-api-version: placement 1.12 + openstack-api-version: placement 1.13 - name: other accept header bad version GET: / diff --git a/nova/tests/unit/api/openstack/placement/test_microversion.py b/nova/tests/unit/api/openstack/placement/test_microversion.py index fa103eb9257d..e5e38cd9885d 100644 --- a/nova/tests/unit/api/openstack/placement/test_microversion.py +++ b/nova/tests/unit/api/openstack/placement/test_microversion.py @@ -74,7 +74,7 @@ class TestMicroversionIntersection(test.NoDBTestCase): # if you add two different versions of method 'foobar' the # number only goes up by one if no other version foobar yet # exists. This operates as a simple sanity check. - TOTAL_VERSIONED_METHODS = 15 + TOTAL_VERSIONED_METHODS = 16 def test_methods_versioned(self): methods_data = microversion.VERSIONED_METHODS diff --git a/placement-api-ref/source/allocations.inc b/placement-api-ref/source/allocations.inc index e85f65d22ae2..8e3a0f9169b1 100644 --- a/placement-api-ref/source/allocations.inc +++ b/placement-api-ref/source/allocations.inc @@ -7,6 +7,51 @@ and used by some consumer of that resource. They indicate the amount of a particular resource that has been allocated to a given consumer of that resource from a particular resource provider. +Manage allocations +================== + +Create, update or delete allocations for multiple consumers in a single +request. This allows a client to atomically set or swap allocations for +multiple consumers as may be required during a migration or move type +operation. + +The allocations for an individual consumer uuid mentioned in the request +can be removed by setting the `allocations` to an empty object (see the +example below). + +**Available as of microversion 1.13.** + +.. rest_method:: POST /allocations + +Normal response codes: 204 + +Error response codes: badRequest(400), conflict(409) + +* `409 Conflict` if there is no available inventory in any of the + resource providers for any specified resource classes or inventories + are updated by another thread while attempting the operation. + +Request +------- + +.. rest_parameters:: parameters.yaml + + - consumer_uuid: consumer_uuid_body + - project_id: project_id_body + - user_id: user_id_body + - allocations: allocations_dict_empty + - resources: resources + +Request Example + +.. literalinclude:: manage-allocations-request.json + :language: javascript + +Response +-------- + +No body content is returned after a successful request + List allocations ================ diff --git a/placement-api-ref/source/manage-allocations-request.json b/placement-api-ref/source/manage-allocations-request.json new file mode 100644 index 000000000000..8bdcb2151d5d --- /dev/null +++ b/placement-api-ref/source/manage-allocations-request.json @@ -0,0 +1,31 @@ +{ + "30328d13-e299-4a93-a102-61e4ccabe474": { + "project_id": "131d4efb-abc0-4872-9b92-8c8b9dc4320f", + "user_id": "131d4efb-abc0-4872-9b92-8c8b9dc4320f", + "allocations": { + "e10927c4-8bc9-465d-ac60-d2f79f7e4a00": { + "resources": { + "VCPU": 2, + "MEMORY_MB": 3 + } + } + } + }, + "71921e4e-1629-4c5b-bf8d-338d915d2ef3": { + "project_id": "131d4efb-abc0-4872-9b92-8c8b9dc4320f", + "user_id": "131d4efb-abc0-4872-9b92-8c8b9dc4320f", + "allocations": {} + }, + "48c1d40f-45d8-4947-8d46-52b4e1326df8": { + "project_id": "131d4efb-abc0-4872-9b92-8c8b9dc4320f", + "user_id": "131d4efb-abc0-4872-9b92-8c8b9dc4320f", + "allocations": { + "e10927c4-8bc9-465d-ac60-d2f79f7e4a00": { + "resources": { + "VCPU": 4, + "MEMORY_MB": 5 + } + } + } + } +} diff --git a/placement-api-ref/source/parameters.yaml b/placement-api-ref/source/parameters.yaml index e59f3a1cbcf8..5c000a701d8e 100644 --- a/placement-api-ref/source/parameters.yaml +++ b/placement-api-ref/source/parameters.yaml @@ -1,5 +1,5 @@ # variables in path -consumer_uuid: +consumer_uuid: &consumer_uuid type: string in: path required: true @@ -147,19 +147,29 @@ allocations_by_resource_provider: required: true description: > A dictionary of allocations keyed by resource provider uuid. -allocations_dict: +allocations_dict: &allocations_dict type: object in: body required: true min_version: 1.12 description: > A dictionary of resource allocations keyed by resource provider uuid. +allocations_dict_empty: + <<: *allocations_dict + description: > + A dictionary of resource allocations keyed by resource provider uuid. + If this is an empty object, allocations for this consumer will be + removed. + min_version: null capacity: type: integer in: body required: true description: > The amount of the resource that the provider can accommodate. +consumer_uuid_body: + <<: *consumer_uuid + in: body inventories: type: object in: body diff --git a/releasenotes/notes/post-allocations-427581fa41671820.yaml b/releasenotes/notes/post-allocations-427581fa41671820.yaml new file mode 100644 index 000000000000..80a9115d4b6c --- /dev/null +++ b/releasenotes/notes/post-allocations-427581fa41671820.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Microversion 1.13 of the Placement API gives the ability to set or clear + allocations for more than one consumer uuid with a request to + ``POST /allocations``.