Merge "placement: support traits in allocation candidates API"

This commit is contained in:
Zuul
2018-01-27 03:34:12 +00:00
committed by Gerrit Code Review
14 changed files with 167 additions and 24 deletions

View File

@@ -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)

View File

@@ -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.
]

View File

@@ -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.

View File

@@ -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"]
}

View File

@@ -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

View File

@@ -2265,7 +2265,7 @@ class PowerVMAPIFailed(NovaException):
class TraitNotFound(NotFound):
msg_fmt = _("No such trait %(name)s.")
msg_fmt = _("No such trait(s): %(name)s.")
class TraitExists(NovaException):

View File

@@ -3515,7 +3515,7 @@ class AllocationCandidates(base.NovaObject):
# Double-check that we found a trait ID for each requested name
if len(trait_map) != len(traits):
missing = traits - set(trait_map)
raise ValueError(_("Unknown traits requested: %s") % missing)
raise exception.TraitNotFound(name=', '.join(missing))
# Contains a set of resource provider IDs that share some inventory for
# each resource class requested. We do this here as an optimization. If

View File

@@ -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,

View File

@@ -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

View File

@@ -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: /

View File

@@ -425,7 +425,7 @@ class AllocationCandidatesTestCase(ProviderDBBase):
requests = [placement_lib.RequestGroup(
use_same_provider=False, resources=self.requested_resources,
required_traits=missing)]
self.assertRaises(ValueError,
self.assertRaises(exception.TraitNotFound,
rp_obj.AllocationCandidates.get_by_requests,
self.ctx, requests)

View File

@@ -31,6 +31,7 @@ Request
- resources: resources_query_required
- limit: allocation_candidates_limit
- required: allocation_candidates_required
Response (microversions 1.12 - )
--------------------------------

View File

@@ -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

View File

@@ -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.