placement: support traits in allocation candidates API
This patch add new query parameter `required` to the `GET /allocation_candidates` API, which is used to filter candidates with required traits. The candidate attached traits return in the provider summary also. Those API changes are added by new microversion. Also using specific exception TraitNotFound instead of the generic exception ValueError when invalid traits in the request. Change-Id: Id821b5b2768dcc698695ba6570c6201e1e9a8233 Implement blueprint add-trait-support-in-allocation-candidates
This commit is contained in:
parent
e9371baf53
commit
dbd7773e05
@ -18,6 +18,7 @@ from oslo_log import log as logging
|
||||
from oslo_serialization import jsonutils
|
||||
from oslo_utils import encodeutils
|
||||
from oslo_utils import timeutils
|
||||
import six
|
||||
import webob
|
||||
|
||||
from nova.api.openstack.placement import microversion
|
||||
@ -117,9 +118,10 @@ def _transform_allocation_requests_list(alloc_reqs):
|
||||
return results
|
||||
|
||||
|
||||
def _transform_provider_summaries(p_sums):
|
||||
def _transform_provider_summaries(p_sums, include_traits=False):
|
||||
"""Turn supplied list of ProviderSummary objects into a dict, keyed by
|
||||
resource provider UUID, of dicts of provider and inventory information.
|
||||
resource provider UUID, of dicts of provider and inventory information. The
|
||||
traits only show up when `include_traits` is `True`.
|
||||
|
||||
{
|
||||
RP_UUID_1: {
|
||||
@ -132,7 +134,11 @@ def _transform_provider_summaries(p_sums):
|
||||
'capacity': 4,
|
||||
'used': 0,
|
||||
}
|
||||
}
|
||||
},
|
||||
'traits': [
|
||||
'HW_CPU_X86_AVX512F',
|
||||
'HW_CPU_X86_AVX512CD'
|
||||
]
|
||||
},
|
||||
RP_UUID_2: {
|
||||
'resources': {
|
||||
@ -144,20 +150,32 @@ def _transform_provider_summaries(p_sums):
|
||||
'capacity': 4,
|
||||
'used': 0,
|
||||
}
|
||||
}
|
||||
},
|
||||
'traits': [
|
||||
'HW_NIC_OFFLOAD_TSO',
|
||||
'HW_NIC_OFFLOAD_GRO'
|
||||
]
|
||||
}
|
||||
}
|
||||
"""
|
||||
return {
|
||||
ps.resource_provider.uuid: {
|
||||
'resources': {
|
||||
psr.resource_class: {
|
||||
'capacity': psr.capacity,
|
||||
'used': psr.used,
|
||||
} for psr in ps.resources
|
||||
}
|
||||
} for ps in p_sums
|
||||
}
|
||||
|
||||
ret = {}
|
||||
|
||||
for ps in p_sums:
|
||||
resources = {
|
||||
psr.resource_class: {
|
||||
'capacity': psr.capacity,
|
||||
'used': psr.used,
|
||||
} for psr in ps.resources
|
||||
}
|
||||
|
||||
ret[ps.resource_provider.uuid] = {'resources': resources}
|
||||
|
||||
if include_traits:
|
||||
ret[ps.resource_provider.uuid]['traits'] = [
|
||||
t.name for t in ps.traits]
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def _transform_allocation_candidates(alloc_cands, want_version):
|
||||
@ -175,7 +193,10 @@ def _transform_allocation_candidates(alloc_cands, want_version):
|
||||
else:
|
||||
a_reqs = _transform_allocation_requests_list(
|
||||
alloc_cands.allocation_requests)
|
||||
p_sums = _transform_provider_summaries(alloc_cands.provider_summaries)
|
||||
|
||||
include_traits = want_version.matches((1, 17))
|
||||
p_sums = _transform_provider_summaries(alloc_cands.provider_summaries,
|
||||
include_traits)
|
||||
return {
|
||||
'allocation_requests': a_reqs,
|
||||
'provider_summaries': p_sums,
|
||||
@ -195,7 +216,9 @@ def list_allocation_candidates(req):
|
||||
context = req.environ['placement.context']
|
||||
want_version = req.environ[microversion.MICROVERSION_ENVIRON]
|
||||
get_schema = schema.GET_SCHEMA_1_10
|
||||
if want_version.matches((1, 16)):
|
||||
if want_version.matches((1, 17)):
|
||||
get_schema = schema.GET_SCHEMA_1_17
|
||||
elif want_version.matches((1, 16)):
|
||||
get_schema = schema.GET_SCHEMA_1_16
|
||||
util.validate_query_params(req, get_schema)
|
||||
|
||||
@ -213,6 +236,8 @@ def list_allocation_candidates(req):
|
||||
raise webob.exc.HTTPBadRequest(
|
||||
_('Invalid resource class in resources parameter: %(error)s') %
|
||||
{'error': exc})
|
||||
except exception.TraitNotFound as exc:
|
||||
raise webob.exc.HTTPBadRequest(six.text_type(exc))
|
||||
|
||||
response = req.response
|
||||
trx_cands = _transform_allocation_candidates(cands, want_version)
|
||||
|
@ -58,6 +58,8 @@ VERSIONS = [
|
||||
# representation and 'in_tree' filter on GET /resource_providers
|
||||
'1.15', # Include last-modified and cache-control headers
|
||||
'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.
|
||||
]
|
||||
|
||||
|
||||
|
@ -214,3 +214,10 @@ header has been added to prevent inadvertent caching of resources.
|
||||
Add support for a ``limit`` query parameter when making a
|
||||
``GET /allocation_candidates`` request. The parameter accepts an integer
|
||||
value, `N`, which limits the maximum number of candidates returned.
|
||||
|
||||
1.17 Add 'required' parameter to the allocation candidates
|
||||
----------------------------------------------------------
|
||||
|
||||
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.
|
||||
|
@ -40,3 +40,9 @@ GET_SCHEMA_1_16['properties']['limit'] = {
|
||||
"minimum": 1,
|
||||
"minLength": 1
|
||||
}
|
||||
|
||||
# Add required parameter.
|
||||
GET_SCHEMA_1_17 = copy.deepcopy(GET_SCHEMA_1_16)
|
||||
GET_SCHEMA_1_17['properties']['required'] = {
|
||||
"type": ["string"]
|
||||
}
|
||||
|
@ -306,8 +306,8 @@ def normalize_traits_qs_param(val):
|
||||
"""
|
||||
ret = set(substr.strip() for substr in val.split(','))
|
||||
if not all(trait for trait in ret):
|
||||
msg = _('Malformed traits parameter. Expected query string value '
|
||||
'of the form: HW_CPU_X86_VMX,CUSTOM_MAGIC. '
|
||||
msg = _('Invalid query string parameters: Expected \'required\' '
|
||||
'parameter value of the form: HW_CPU_X86_VMX,CUSTOM_MAGIC. '
|
||||
'Got: "%s"') % val
|
||||
raise webob.exc.HTTPBadRequest(msg)
|
||||
return ret
|
||||
|
@ -271,6 +271,10 @@ class SharedStorageFixture(APIFixture):
|
||||
inv_list = rp_obj.InventoryList(objects=[vcpu_inv, ram_inv])
|
||||
cn.set_inventory(inv_list)
|
||||
|
||||
t_avx_sse = rp_obj.Trait.get_by_name(self.context, "HW_CPU_X86_SSE")
|
||||
t_avx_sse2 = rp_obj.Trait.get_by_name(self.context, "HW_CPU_X86_SSE2")
|
||||
cn1.set_traits(rp_obj.TraitList(objects=[t_avx_sse, t_avx_sse2]))
|
||||
|
||||
# Populate shared storage provider with DISK_GB inventory
|
||||
disk_inv = rp_obj.Inventory(
|
||||
self.context,
|
||||
|
@ -170,3 +170,80 @@ tests:
|
||||
openstack-api-version: placement 1.16
|
||||
response_json_paths:
|
||||
$.allocation_requests.`len`: 1
|
||||
|
||||
- name: get allocation candidates with required traits in old version
|
||||
GET: /allocation_candidates?resources=VCPU:1,MEMORY_MB:1024,DISK_GB:100&required=HW_CPU_X86_SSE
|
||||
status: 400
|
||||
request_headers:
|
||||
openstack-api-version: placement 1.16
|
||||
response_strings:
|
||||
- Invalid query string parameters
|
||||
- "'required' was unexpected"
|
||||
|
||||
- name: get allocation candidates without traits summary in old version
|
||||
GET: /allocation_candidates?resources=VCPU:1,MEMORY_MB:1024,DISK_GB:100
|
||||
status: 200
|
||||
request_headers:
|
||||
openstack-api-version: placement 1.16
|
||||
response_json_paths:
|
||||
$.provider_summaries["$ENVIRON['CN1_UUID']"].`len`: 1
|
||||
$.provider_summaries["$ENVIRON['CN2_UUID']"].`len`: 1
|
||||
|
||||
- name: get allocation candidates with invalid trait
|
||||
GET: /allocation_candidates?resources=VCPU:1,MEMORY_MB:1024,DISK_GB:100&required=INVALID_TRAIT
|
||||
status: 400
|
||||
request_headers:
|
||||
openstack-api-version: placement 1.17
|
||||
response_strings:
|
||||
- No such trait(s)
|
||||
|
||||
- name: get allocation candidates with empty required value
|
||||
GET: /allocation_candidates?resources=VCPU:1,MEMORY_MB:1024,DISK_GB:100&required=
|
||||
status: 400
|
||||
request_headers:
|
||||
openstack-api-version: placement 1.17
|
||||
response_strings:
|
||||
- "Invalid query string parameters: Expected 'required' parameter value of the form: HW_CPU_X86_VMX,CUSTOM_MAGIC."
|
||||
|
||||
- name: get allocation candidates with invalid required value
|
||||
GET: /allocation_candidates?resources=VCPU:1,MEMORY_MB:1024,DISK_GB:100&required=,,
|
||||
status: 400
|
||||
request_headers:
|
||||
openstack-api-version: placement 1.17
|
||||
response_strings:
|
||||
- "Invalid query string parameters: Expected 'required' parameter value of the form: HW_CPU_X86_VMX,CUSTOM_MAGIC."
|
||||
|
||||
- name: get allocation candidates with required trait
|
||||
GET: /allocation_candidates?resources=VCPU:1,MEMORY_MB:1024,DISK_GB:100&required=HW_CPU_X86_SSE
|
||||
status: 200
|
||||
request_headers:
|
||||
openstack-api-version: placement 1.17
|
||||
response_json_paths:
|
||||
$.allocation_requests.`len`: 1
|
||||
$.provider_summaries.`len`: 2
|
||||
$.provider_summaries["$ENVIRON['CN1_UUID']"].`len`: 2
|
||||
$.provider_summaries["$ENVIRON['CN1_UUID']"].traits.`sorted`:
|
||||
- HW_CPU_X86_SSE
|
||||
- HW_CPU_X86_SSE2
|
||||
|
||||
- name: get allocation candidates with multiple required traits
|
||||
GET: /allocation_candidates?resources=VCPU:1,MEMORY_MB:1024,DISK_GB:100&required=HW_CPU_X86_SSE,HW_CPU_X86_SSE2
|
||||
status: 200
|
||||
request_headers:
|
||||
openstack-api-version: placement 1.17
|
||||
response_json_paths:
|
||||
$.allocation_requests.`len`: 1
|
||||
$.provider_summaries.`len`: 2
|
||||
$.provider_summaries["$ENVIRON['CN1_UUID']"].`len`: 2
|
||||
$.provider_summaries["$ENVIRON['CN1_UUID']"].traits.`sorted`:
|
||||
- HW_CPU_X86_SSE
|
||||
- HW_CPU_X86_SSE2
|
||||
|
||||
- name: get allocation candidates with required trait and no matching
|
||||
GET: /allocation_candidates?resources=VCPU:1,MEMORY_MB:1024,DISK_GB:100&required=HW_CPU_X86_SSE3
|
||||
status: 200
|
||||
request_headers:
|
||||
openstack-api-version: placement 1.17
|
||||
response_json_paths:
|
||||
$.allocation_requests.`len`: 0
|
||||
$.provider_summaries.`len`: 0
|
||||
|
@ -39,13 +39,13 @@ tests:
|
||||
response_json_paths:
|
||||
$.errors[0].title: Not Acceptable
|
||||
|
||||
- name: latest microversion is 1.16
|
||||
- name: latest microversion is 1.17
|
||||
GET: /
|
||||
request_headers:
|
||||
openstack-api-version: placement latest
|
||||
response_headers:
|
||||
vary: /OpenStack-API-Version/
|
||||
openstack-api-version: placement 1.16
|
||||
openstack-api-version: placement 1.17
|
||||
|
||||
- name: other accept header bad version
|
||||
GET: /
|
||||
|
@ -31,6 +31,7 @@ Request
|
||||
|
||||
- resources: resources_query_required
|
||||
- limit: allocation_candidates_limit
|
||||
- required: allocation_candidates_required
|
||||
|
||||
Response (microversions 1.12 - )
|
||||
--------------------------------
|
||||
|
@ -50,6 +50,16 @@ allocation_candidates_limit:
|
||||
description: >
|
||||
A positive integer used to limit the maximum number of allocation
|
||||
candidates returned in the response.
|
||||
allocation_candidates_required:
|
||||
type: string
|
||||
in: query
|
||||
required: false
|
||||
min_version: 1.17
|
||||
description: >
|
||||
Accepts a list of traits separated by `,`. Allocation requests in the
|
||||
response will be for resource providers that have capacity for all
|
||||
requested resources and the set of those resource providers will
|
||||
*collectively* contain all of the required traits.
|
||||
member_of:
|
||||
type: string
|
||||
in: query
|
||||
@ -241,7 +251,8 @@ provider_summaries:
|
||||
required: true
|
||||
description: >
|
||||
A dictionary keyed by resource provider UUID,
|
||||
of dictionaries of inventory/capacity information.
|
||||
of dictionaries of inventory/capacity information. The list of traits
|
||||
the resource provider has associated with it is included in version `1.17`.
|
||||
reserved: &reserved
|
||||
type: integer
|
||||
in: body
|
||||
|
@ -0,0 +1,10 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Add ``required`` query parameter to the ``GET /allocation_candidates`` API
|
||||
in new placement microversion 1.17. The parameter accepts a list of traits
|
||||
separated by ``,``, which is used to further limit the list of allocation
|
||||
requests to resource providers that have the capacity to fulfill the
|
||||
requested resources AND *collectively* have all of the required traits
|
||||
associated with them. In the same microversion, the candidate attached
|
||||
traits returned in the provider summary.
|
Loading…
Reference in New Issue
Block a user