From 8e797aad7c181d2d782ea44958d255e7e9400f19 Mon Sep 17 00:00:00 2001 From: Chris Dent Date: Tue, 6 Dec 2016 22:30:47 +0000 Subject: [PATCH] HTTP interface for resource providers by aggregates In a new 1.3 microversion, the GET /resource_providers handler gains support for a new query parameter 'member_of' which takes a value of 'in:' and a comma separated list of aggregate uuids, or a single aggregate uuid. The response is the list of resource providers that are associated with any of those aggregates, or an empty list if there are none. If in an old microversion, the query parameter is not accepted and a 400 is returned. Change-Id: I82fc2003ce85dcadfecfea506e7d4adb47258c7a --- .../placement/handlers/resource_provider.py | 17 ++- nova/api/openstack/placement/microversion.py | 2 + .../placement/rest_api_version_history.rst | 8 ++ .../placement/gabbits/microversion.yaml | 4 +- .../gabbits/resource-provider-aggregates.yaml | 100 ++++++++++++++++++ ...cement-api-member-of-d8a08d0d0c5700d7.yaml | 14 +++ 6 files changed, 142 insertions(+), 3 deletions(-) create mode 100644 nova/tests/functional/api/openstack/placement/gabbits/resource-provider-aggregates.yaml create mode 100644 releasenotes/notes/placement-api-member-of-d8a08d0d0c5700d7.yaml diff --git a/nova/api/openstack/placement/handlers/resource_provider.py b/nova/api/openstack/placement/handlers/resource_provider.py index d8a895e79..703169dd8 100644 --- a/nova/api/openstack/placement/handlers/resource_provider.py +++ b/nova/api/openstack/placement/handlers/resource_provider.py @@ -18,6 +18,7 @@ from oslo_serialization import jsonutils from oslo_utils import uuidutils import webob +from nova.api.openstack.placement import microversion from nova.api.openstack.placement import util from nova import exception from nova.i18n import _ @@ -162,8 +163,11 @@ def list_resource_providers(req): a collection of resource providers. """ context = req.environ['placement.context'] + want_version = req.environ[microversion.MICROVERSION_ENVIRON] allowed_filters = set(objects.ResourceProviderList.allowed_filters) + if not want_version.matches((1, 3)): + allowed_filters.remove('member_of') passed_filters = set(req.GET.keys()) invalid_filters = passed_filters - allowed_filters if invalid_filters: @@ -180,7 +184,18 @@ def list_resource_providers(req): filters = {} for attr in objects.ResourceProviderList.allowed_filters: if attr in req.GET: - filters[attr] = req.GET[attr] + value = req.GET[attr] + # special case member_of to always make its value a + # list, either by accepting the single value, or if it + # starts with 'in:' splitting on ','. + # NOTE(cdent): This will all change when we start using + # JSONSchema validation of query params. + if attr == 'member_of': + if value.startswith('in:'): + value = value[3:].split(',') + else: + value = [value] + filters[attr] = value resource_providers = objects.ResourceProviderList.get_all_by_filters( context, filters) diff --git a/nova/api/openstack/placement/microversion.py b/nova/api/openstack/placement/microversion.py index 72c857822..d293dcf1f 100644 --- a/nova/api/openstack/placement/microversion.py +++ b/nova/api/openstack/placement/microversion.py @@ -37,6 +37,8 @@ VERSIONS = [ '1.0', '1.1', # initial support for aggregate.get_aggregates and set_aggregates '1.2', # Adds /resource_classes resource endpoint + '1.3', # Adds 'member_of' query parameter to get resource providers + # that are members of any of the listed aggregates ] diff --git a/nova/api/openstack/placement/rest_api_version_history.rst b/nova/api/openstack/placement/rest_api_version_history.rst index b500dd1d4..2810af5d6 100644 --- a/nova/api/openstack/placement/rest_api_version_history.rst +++ b/nova/api/openstack/placement/rest_api_version_history.rst @@ -44,3 +44,11 @@ The following new routes are added: Custom resource classes must begin with the prefix "CUSTOM\_" and contain only the letters A through Z, the numbers 0 through 9 and the underscore "\_" character. + +1.3 member_of query parameter +----------------------------- + +Version 1.3 adds support for listing resource providers that are members of +any of the list of aggregates provided using a ``member_of`` query parameter: + +* /resource_providers?member_of=in:{agg1_uuid},{agg2_uuid},{agg3_uuid} diff --git a/nova/tests/functional/api/openstack/placement/gabbits/microversion.yaml b/nova/tests/functional/api/openstack/placement/gabbits/microversion.yaml index 32a4f4fb9..56a6729e1 100644 --- a/nova/tests/functional/api/openstack/placement/gabbits/microversion.yaml +++ b/nova/tests/functional/api/openstack/placement/gabbits/microversion.yaml @@ -37,13 +37,13 @@ tests: response_strings: - "Unacceptable version header: 0.5" -- name: latest microversion is 1.2 +- name: latest microversion is 1.3 GET: / request_headers: openstack-api-version: placement latest response_headers: vary: /OpenStack-API-Version/ - openstack-api-version: placement 1.2 + openstack-api-version: placement 1.3 - name: other accept header bad version GET: / diff --git a/nova/tests/functional/api/openstack/placement/gabbits/resource-provider-aggregates.yaml b/nova/tests/functional/api/openstack/placement/gabbits/resource-provider-aggregates.yaml new file mode 100644 index 000000000..044806803 --- /dev/null +++ b/nova/tests/functional/api/openstack/placement/gabbits/resource-provider-aggregates.yaml @@ -0,0 +1,100 @@ +# Tests filtering resource providers by aggregates + +fixtures: + - APIFixture + +defaults: + request_headers: + x-auth-token: admin + content-type: application/json + accept: application/json + openstack-api-version: placement latest + +tests: + +- name: post new provider 1 + POST: /resource_providers + data: + name: rp_1 + uuid: 893337e9-1e55-49f0-bcfe-6a2f16fbf2f7 + status: 201 + +- name: post new provider 2 + POST: /resource_providers + data: + name: rp_2 + uuid: 5202c48f-c960-4eec-bde3-89c4f22a17b9 + status: 201 + +- name: get by aggregates no result + GET: '/resource_providers?member_of=in:83a3d69d-8920-48e2-8914-cadfd8fa2f91' + response_json_paths: + $.resource_providers: [] + +- name: associate an aggregate with rp1 + PUT: /resource_providers/893337e9-1e55-49f0-bcfe-6a2f16fbf2f7/aggregates + data: + - 83a3d69d-8920-48e2-8914-cadfd8fa2f91 + status: 200 + +- name: get by aggregates one result + GET: '/resource_providers?member_of=in:83a3d69d-8920-48e2-8914-cadfd8fa2f91' + response_json_paths: + $.resource_providers[0].uuid: 893337e9-1e55-49f0-bcfe-6a2f16fbf2f7 + +- name: get by aggregates one result no in + GET: '/resource_providers?member_of=83a3d69d-8920-48e2-8914-cadfd8fa2f91' + response_json_paths: + $.resource_providers[0].uuid: 893337e9-1e55-49f0-bcfe-6a2f16fbf2f7 + +- name: associate an aggregate with rp2 + PUT: /resource_providers/5202c48f-c960-4eec-bde3-89c4f22a17b9/aggregates + data: + - 83a3d69d-8920-48e2-8914-cadfd8fa2f91 + status: 200 + +- name: get by aggregates two result + GET: '/resource_providers?member_of=in:83a3d69d-8920-48e2-8914-cadfd8fa2f91' + response_json_paths: + $.resource_providers.`len`: 2 + $.resource_providers[0].uuid: /5202c48f-c960-4eec-bde3-89c4f22a17b9|893337e9-1e55-49f0-bcfe-6a2f16fbf2f7/ + $.resource_providers[1].uuid: /5202c48f-c960-4eec-bde3-89c4f22a17b9|893337e9-1e55-49f0-bcfe-6a2f16fbf2f7/ + +- name: associate another aggregate with rp2 + PUT: /resource_providers/5202c48f-c960-4eec-bde3-89c4f22a17b9/aggregates + data: + - 99652f11-9f77-46b9-80b7-4b1989be9f8c + status: 200 + +- name: get by both aggregates two + GET: '/resource_providers?member_of=in:83a3d69d-8920-48e2-8914-cadfd8fa2f91,99652f11-9f77-46b9-80b7-4b1989be9f8c' + response_json_paths: + $.resource_providers.`len`: 2 + $.resource_providers[0].uuid: /5202c48f-c960-4eec-bde3-89c4f22a17b9|893337e9-1e55-49f0-bcfe-6a2f16fbf2f7/ + $.resource_providers[1].uuid: /5202c48f-c960-4eec-bde3-89c4f22a17b9|893337e9-1e55-49f0-bcfe-6a2f16fbf2f7/ + +- name: clear aggregates on rp1 + PUT: /resource_providers/893337e9-1e55-49f0-bcfe-6a2f16fbf2f7/aggregates + data: [] + status: 200 + +- name: get by both aggregates one + desc: only one result because we disassociated aggregates in the PUT above + GET: '/resource_providers?member_of=in:83a3d69d-8920-48e2-8914-cadfd8fa2f91,99652f11-9f77-46b9-80b7-4b1989be9f8c' + response_json_paths: + $.resource_providers.`len`: 1 + $.resource_providers[0].uuid: 5202c48f-c960-4eec-bde3-89c4f22a17b9 + +- name: error on old microverison + GET: '/resource_providers?member_of=in:83a3d69d-8920-48e2-8914-cadfd8fa2f91,99652f11-9f77-46b9-80b7-4b1989be9f8c' + request_headers: + openstack-api-version: placement 1.1 + status: 400 + response_json_paths: + $.errors[0].detail: '/Invalid filters: member_of/' + +- name: error on bogus query parameter + GET: '/resource_providers?assoc_with_aggregate=in:83a3d69d-8920-48e2-8914-cadfd8fa2f91,99652f11-9f77-46b9-80b7-4b1989be9f8c' + status: 400 + response_json_paths: + $.errors[0].detail: '/Invalid filters: assoc_with_aggregate/' diff --git a/releasenotes/notes/placement-api-member-of-d8a08d0d0c5700d7.yaml b/releasenotes/notes/placement-api-member-of-d8a08d0d0c5700d7.yaml new file mode 100644 index 000000000..7f1045ffc --- /dev/null +++ b/releasenotes/notes/placement-api-member-of-d8a08d0d0c5700d7.yaml @@ -0,0 +1,14 @@ +--- +features: + - | + A new Placement API microversion 1.3 is added with support for filtering + the list of resource providers to include only those resource providers + which are members of any of the aggregates listed by uuid in the `member_of` + query parameter. The parameter is used when making a + `GET /resource_providers` request. The value of the parameter uses the + `in:` syntax to provide a list of aggregate uuids as follows:: + + /resource_providers?member_of=in:09c931b0-c0d7-4e80-8e01-9e6511db8259,f8ab4fa2-804f-402e-b675-7918bd04b173 + + If other filtering query parameters are present, the results are a boolean + AND of all the filters.