rp: GET /resource_providers?required=<traits>
Introduce placement microversion 1.18 with a new ?required=<trait list> query parameter accepted on the GET /resource_providers API. Results are filtered by providers possessing *all* of the specified traits. Empty/invalid traits result in 400 errors. Change-Id: I8191c9a390cb02b2a38a3f1c6e29457435994981 blueprint: traits-on-list-resource-providers
This commit is contained in:
parent
ef4000a0d3
commit
558540a27c
@ -174,7 +174,9 @@ def list_resource_providers(req):
|
||||
want_version = req.environ[microversion.MICROVERSION_ENVIRON]
|
||||
|
||||
schema = rp_schema.GET_RPS_SCHEMA_1_0
|
||||
if want_version.matches((1, 14)):
|
||||
if want_version.matches((1, 18)):
|
||||
schema = rp_schema.GET_RPS_SCHEMA_1_18
|
||||
elif want_version.matches((1, 14)):
|
||||
schema = rp_schema.GET_RPS_SCHEMA_1_14
|
||||
elif want_version.matches((1, 4)):
|
||||
schema = rp_schema.GET_RPS_SCHEMA_1_4
|
||||
@ -184,7 +186,8 @@ def list_resource_providers(req):
|
||||
util.validate_query_params(req, schema)
|
||||
|
||||
filters = {}
|
||||
for attr in ['uuid', 'name', 'member_of', 'in_tree']:
|
||||
qpkeys = ('uuid', 'name', 'member_of', 'in_tree', 'resources', 'required')
|
||||
for attr in qpkeys:
|
||||
if attr in req.GET:
|
||||
value = req.GET[attr]
|
||||
# special case member_of to always make its value a
|
||||
@ -203,10 +206,11 @@ def list_resource_providers(req):
|
||||
raise webob.exc.HTTPBadRequest(
|
||||
_('Invalid uuid value: %(uuid)s') %
|
||||
{'uuid': aggr_uuid})
|
||||
elif attr == 'resources':
|
||||
value = util.normalize_resources_qs_param(value)
|
||||
elif attr == 'required':
|
||||
value = util.normalize_traits_qs_param(value)
|
||||
filters[attr] = value
|
||||
if 'resources' in req.GET:
|
||||
resources = util.normalize_resources_qs_param(req.GET['resources'])
|
||||
filters['resources'] = resources
|
||||
try:
|
||||
resource_providers = rp_obj.ResourceProviderList.get_all_by_filters(
|
||||
context, filters)
|
||||
@ -214,6 +218,10 @@ def list_resource_providers(req):
|
||||
raise webob.exc.HTTPBadRequest(
|
||||
_('Invalid resource class in resources parameter: %(error)s') %
|
||||
{'error': exc})
|
||||
except exception.TraitNotFound as exc:
|
||||
raise webob.exc.HTTPBadRequest(
|
||||
_('Invalid trait(s) in "required" parameter: %(error)s') %
|
||||
{'error': exc})
|
||||
|
||||
response = req.response
|
||||
output, last_modified = _serialize_providers(
|
||||
|
@ -60,6 +60,7 @@ VERSIONS = [
|
||||
'1.16', # Add 'limit' query parameter to GET /allocation_candidates
|
||||
'1.17', # Add 'required' query parameter to GET /allocation_candidates and
|
||||
# return traits in the provider summary.
|
||||
'1.18', # Support ?required=<traits> queryparam on GET /resource_providers
|
||||
]
|
||||
|
||||
|
||||
|
@ -158,7 +158,7 @@ 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.
|
||||
``GET /resource_providers`` response. The link is included as of version 1.11.
|
||||
|
||||
1.12 PUT dict format to /allocations/{consumer_uuid}
|
||||
----------------------------------------------------
|
||||
@ -221,3 +221,15 @@ value, `N`, which limits the maximum number of candidates returned.
|
||||
Add the `required` parameter to the `GET /allocation_candidates` API. It
|
||||
accepts a list of traits separated by `,`. The provider summary in the response
|
||||
will include the attached traits also.
|
||||
|
||||
1.18 Support ?required=<traits> queryparam on GET /resource_providers
|
||||
---------------------------------------------------------------------
|
||||
|
||||
Add support for the `required` query parameter to the `GET /resource_providers`
|
||||
API. It accepts a comma-separated list of string trait names. When specified,
|
||||
the API results will be filtered to include only resource providers marked with
|
||||
all the specified traits. This is in addition to (logical AND) any filtering
|
||||
based on other query parameters.
|
||||
|
||||
Trait names which are empty, do not exist, or are otherwise invalid will result
|
||||
in a 400 error.
|
||||
|
@ -94,3 +94,13 @@ GET_RPS_SCHEMA_1_14['properties']['in_tree'] = {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
}
|
||||
|
||||
# Microversion 1.18 adds support for the `required` query parameter to the
|
||||
# `GET /resource_providers` API. It accepts a comma-separated list of string
|
||||
# trait names. When specified, the API results will be filtered to include only
|
||||
# resource providers marked with all the specified traits. This is in addition
|
||||
# to (logical AND) any filtering based on other query parameters.
|
||||
GET_RPS_SCHEMA_1_18 = copy.deepcopy(GET_RPS_SCHEMA_1_14)
|
||||
GET_RPS_SCHEMA_1_18['properties']['required'] = {
|
||||
"type": "string",
|
||||
}
|
||||
|
@ -1396,6 +1396,7 @@ class ResourceProviderList(base.ObjectListBase, base.NovaObject):
|
||||
# 'MEMORY_MB': 1024
|
||||
# },
|
||||
# 'in_tree': <uuid>,
|
||||
# 'required': [<trait_name>, ...]
|
||||
# }
|
||||
if not filters:
|
||||
filters = {}
|
||||
@ -1406,6 +1407,7 @@ class ResourceProviderList(base.ObjectListBase, base.NovaObject):
|
||||
name = filters.pop('name', None)
|
||||
uuid = filters.pop('uuid', None)
|
||||
member_of = filters.pop('member_of', [])
|
||||
required = filters.pop('required', [])
|
||||
|
||||
resources = filters.pop('resources', {})
|
||||
# NOTE(sbauza): We want to key the dict by the resource class IDs
|
||||
@ -1473,6 +1475,19 @@ class ResourceProviderList(base.ObjectListBase, base.NovaObject):
|
||||
[resource_provider_id]).select_from(join_statement)
|
||||
query = query.where(rp.c.id.in_(rps_in_aggregates))
|
||||
|
||||
# If 'required' has values, add a filter to limit results to providers
|
||||
# possessing *all* of the listed traits.
|
||||
if required:
|
||||
trait_map = _trait_ids_from_names(context, required)
|
||||
if len(trait_map) != len(required):
|
||||
missing = required - set(trait_map)
|
||||
raise exception.TraitNotFound(names=', '.join(missing))
|
||||
rp_ids = _get_provider_ids_having_all_traits(context, trait_map)
|
||||
if not rp_ids:
|
||||
# If no providers have the required traits, we're done
|
||||
return []
|
||||
query = query.where(rp.c.id.in_(rp_ids))
|
||||
|
||||
if not resources:
|
||||
# Returns quickly the list in case we don't need to check the
|
||||
# resource usage
|
||||
@ -1575,6 +1590,7 @@ class ResourceProviderList(base.ObjectListBase, base.NovaObject):
|
||||
:type filters: dict
|
||||
"""
|
||||
_ensure_rc_cache(context)
|
||||
_ensure_trait_sync(context)
|
||||
resource_providers = cls._get_all_by_filters_from_db(context, filters)
|
||||
return base.obj_make_list(context, cls(context),
|
||||
ResourceProvider, resource_providers)
|
||||
|
@ -39,13 +39,13 @@ tests:
|
||||
response_json_paths:
|
||||
$.errors[0].title: Not Acceptable
|
||||
|
||||
- name: latest microversion is 1.17
|
||||
- name: latest microversion is 1.18
|
||||
GET: /
|
||||
request_headers:
|
||||
openstack-api-version: placement latest
|
||||
response_headers:
|
||||
vary: /OpenStack-API-Version/
|
||||
openstack-api-version: placement 1.17
|
||||
openstack-api-version: placement 1.18
|
||||
|
||||
- name: other accept header bad version
|
||||
GET: /
|
||||
|
@ -392,6 +392,107 @@ tests:
|
||||
$.resource_providers.`len`: 1
|
||||
$.resource_providers[?uuid="$ENVIRON['ALT_PARENT_PROVIDER_UUID']"].root_provider_uuid: $ENVIRON['ALT_PARENT_PROVIDER_UUID']
|
||||
|
||||
- name: filter providers by traits none of them have
|
||||
GET: /resource_providers?required=HW_CPU_X86_SGX,HW_CPU_X86_SHA
|
||||
response_json_paths:
|
||||
$.resource_providers.`len`: 0
|
||||
|
||||
- name: add traits to a provider
|
||||
PUT: /resource_providers/$ENVIRON['RP_UUID']/traits
|
||||
request_headers:
|
||||
content-type: application/json
|
||||
data:
|
||||
resource_provider_generation: 0
|
||||
traits: ['HW_CPU_X86_SGX', 'STORAGE_DISK_SSD']
|
||||
|
||||
- name: add traits to another provider
|
||||
PUT: /resource_providers/$ENVIRON['ALT_PARENT_PROVIDER_UUID']/traits
|
||||
request_headers:
|
||||
content-type: application/json
|
||||
data:
|
||||
resource_provider_generation: 0
|
||||
traits: ['MISC_SHARES_VIA_AGGREGATE', 'STORAGE_DISK_SSD']
|
||||
|
||||
- name: filter providers with multiple traits where no provider has all of them
|
||||
GET: /resource_providers?required=HW_CPU_X86_SGX,MISC_SHARES_VIA_AGGREGATE
|
||||
response_json_paths:
|
||||
$.resource_providers.`len`: 0
|
||||
|
||||
- name: filter providers with a trait some of them have
|
||||
GET: /resource_providers?required=STORAGE_DISK_SSD
|
||||
response_json_paths:
|
||||
$.resource_providers.`len`: 2
|
||||
# Don't really care about the root UUID - just validating that the providers present are the ones we expected
|
||||
$.resource_providers[?uuid="$ENVIRON['ALT_PARENT_PROVIDER_UUID']"].root_provider_uuid: $ENVIRON['ALT_PARENT_PROVIDER_UUID']
|
||||
$.resource_providers[?uuid="$ENVIRON['RP_UUID']"].root_provider_uuid: $ENVIRON['PARENT_PROVIDER_UUID']
|
||||
|
||||
- name: list providers with 'required' parameter filters cumulatively with in_tree
|
||||
GET: /resource_providers?required=STORAGE_DISK_SSD&in_tree=$ENVIRON['RP_UUID']
|
||||
response_json_paths:
|
||||
$.resource_providers.`len`: 1
|
||||
# Only RP_UUID satisfies both the tree and trait constraint
|
||||
$.resource_providers[?uuid="$ENVIRON['RP_UUID']"].root_provider_uuid: $ENVIRON['PARENT_PROVIDER_UUID']
|
||||
|
||||
- name: create some inventory
|
||||
PUT: /resource_providers/$ENVIRON['ALT_PARENT_PROVIDER_UUID']/inventories
|
||||
request_headers:
|
||||
content-type: application/json
|
||||
data:
|
||||
resource_provider_generation: 1
|
||||
inventories:
|
||||
IPV4_ADDRESS:
|
||||
total: 253
|
||||
DISK_GB:
|
||||
total: 1024
|
||||
status: 200
|
||||
response_json_paths:
|
||||
$.resource_provider_generation: 2
|
||||
$.inventories.IPV4_ADDRESS.total: 253
|
||||
$.inventories.IPV4_ADDRESS.reserved: 0
|
||||
$.inventories.DISK_GB.total: 1024
|
||||
$.inventories.DISK_GB.allocation_ratio: 1.0
|
||||
|
||||
- name: list providers with 'required' parameter filters cumulatively with resources
|
||||
GET: /resource_providers?required=STORAGE_DISK_SSD&resources=IPV4_ADDRESS:10
|
||||
response_json_paths:
|
||||
$.resource_providers.`len`: 1
|
||||
# Only ALT_PARENT_PROVIDER_UUID satisfies both the tree and trait constraint
|
||||
$.resource_providers[?uuid="$ENVIRON['ALT_PARENT_PROVIDER_UUID']"].root_provider_uuid: $ENVIRON['ALT_PARENT_PROVIDER_UUID']
|
||||
|
||||
- name: invalid 'required' parameter - blank
|
||||
GET: /resource_providers?required=
|
||||
status: 400
|
||||
response_strings:
|
||||
- "Invalid query string parameters: Expected 'required' parameter value of the form: HW_CPU_X86_VMX,CUSTOM_MAGIC."
|
||||
response_json_paths:
|
||||
$.errors[0].title: Bad Request
|
||||
|
||||
- name: invalid 'required' parameter - contains an empty trait name
|
||||
GET: /resource_providers?required=STORAGE_DISK_SSD,,MISC_SHARES_VIA_AGGREGATE
|
||||
status: 400
|
||||
response_strings:
|
||||
- "Invalid query string parameters: Expected 'required' parameter value of the form: HW_CPU_X86_VMX,CUSTOM_MAGIC."
|
||||
response_json_paths:
|
||||
$.errors[0].title: Bad Request
|
||||
|
||||
- name: invalid 'required' parameter - contains a nonexistent trait
|
||||
GET: /resource_providers?required=STORAGE_DISK_SSD,BOGUS_TRAIT,MISC_SHARES_VIA_AGGREGATE
|
||||
status: 400
|
||||
response_strings:
|
||||
- "No such trait(s): BOGUS_TRAIT."
|
||||
response_json_paths:
|
||||
$.errors[0].title: Bad Request
|
||||
|
||||
- name: schema validation fails with 'required' parameter on old microversion
|
||||
request_headers:
|
||||
openstack-api-version: placement 1.17
|
||||
GET: /resource_providers?required=HW_CPU_X86_SGX,MISC_SHARES_VIA_AGGREGATE
|
||||
status: 400
|
||||
response_strings:
|
||||
- Additional properties are not allowed
|
||||
response_json_paths:
|
||||
$.errors[0].title: Bad Request
|
||||
|
||||
- name: fail trying to re-parent to a different provider
|
||||
PUT: /resource_providers/$ENVIRON['RP_UUID']
|
||||
request_headers:
|
||||
|
@ -85,6 +85,14 @@ resource_provider_name_query:
|
||||
required: false
|
||||
description: >
|
||||
The name of a resource provider to filter the list.
|
||||
resource_provider_required_query:
|
||||
type: string
|
||||
in: query
|
||||
required: false
|
||||
description: >
|
||||
A comma-delimited list of string trait names. Results will be filtered to
|
||||
include only resource providers having all the specified traits.
|
||||
min_version: 1.18
|
||||
resource_provider_tree_query:
|
||||
type: string
|
||||
in: query
|
||||
|
@ -28,6 +28,7 @@ of all filters are merged with a boolean `AND`.
|
||||
- uuid: resource_provider_uuid_query
|
||||
- member_of: member_of
|
||||
- resources: resources_query
|
||||
- required: resource_provider_required_query
|
||||
- in_tree: resource_provider_tree_query
|
||||
|
||||
Response
|
||||
|
@ -0,0 +1,12 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Placement API microversion 1.18 adds support for the `required` query
|
||||
parameter to the `GET /resource_providers` API. It accepts a
|
||||
comma-separated list of string trait names. When specified, the API
|
||||
results will be filtered to include only resource providers marked with
|
||||
all the specified traits. This is in addition to (logical AND) any
|
||||
filtering based on other query parameters.
|
||||
|
||||
Trait names which are empty, do not exist, or are otherwise invalid will
|
||||
result in a 400 error.
|
Loading…
Reference in New Issue
Block a user