Return all resources in provider_summaries
The response of ``GET /allocation_candidates`` API provides two fields of ``allocation_requests`` and ``provider_summaries``. The callers, like the filter scheduler in nova, would use information in ``provider_summaries`` in sorting or filtering providers to allocate consumers. However, currently ``provider_summaries`` doesn't contain resource classes that aren't requested. With this patch, ``GET /allocation_candidates`` API returns all resource classes with a new microversion. Change-Id: Ic491f190ebd97d94c18931a0e78d779a55ee47a1 Closes-Bug: #1760276 Blueprint: placement-return-all-resources
This commit is contained in:
parent
10b03bbe01
commit
a40f6b08fa
@ -116,10 +116,14 @@ def _transform_allocation_requests_list(alloc_reqs):
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
def _transform_provider_summaries(p_sums, include_traits=False):
|
def _transform_provider_summaries(p_sums, requests, include_traits=False,
|
||||||
|
include_all_resources=False):
|
||||||
"""Turn supplied list of ProviderSummary objects into a dict, keyed by
|
"""Turn supplied list of ProviderSummary objects into a dict, keyed by
|
||||||
resource provider UUID, of dicts of provider and inventory information. The
|
resource provider UUID, of dicts of provider and inventory information.
|
||||||
traits only show up when `include_traits` is `True`.
|
The traits only show up when `include_traits` is `True`.
|
||||||
|
When `include_all_resources` is `True`, all the resource classes are
|
||||||
|
shown while only requested resources are included in the
|
||||||
|
`provider_summaries` when `include_all_resources` is `False`.
|
||||||
|
|
||||||
{
|
{
|
||||||
RP_UUID_1: {
|
RP_UUID_1: {
|
||||||
@ -158,13 +162,21 @@ def _transform_provider_summaries(p_sums, include_traits=False):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
ret = {}
|
ret = {}
|
||||||
|
requested_resources = set()
|
||||||
|
|
||||||
|
for requested_group in requests.values():
|
||||||
|
requested_resources |= set(requested_group.resources.keys())
|
||||||
|
|
||||||
|
# if include_all_resources is false, only requested resources are
|
||||||
|
# included in the provider_summaries.
|
||||||
for ps in p_sums:
|
for ps in p_sums:
|
||||||
resources = {
|
resources = {
|
||||||
psr.resource_class: {
|
psr.resource_class: {
|
||||||
'capacity': psr.capacity,
|
'capacity': psr.capacity,
|
||||||
'used': psr.used,
|
'used': psr.used,
|
||||||
} for psr in ps.resources
|
} for psr in ps.resources if (
|
||||||
|
include_all_resources or
|
||||||
|
psr.resource_class in requested_resources)
|
||||||
}
|
}
|
||||||
|
|
||||||
ret[ps.resource_provider.uuid] = {'resources': resources}
|
ret[ps.resource_provider.uuid] = {'resources': resources}
|
||||||
@ -176,7 +188,7 @@ def _transform_provider_summaries(p_sums, include_traits=False):
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def _transform_allocation_candidates(alloc_cands, want_version):
|
def _transform_allocation_candidates(alloc_cands, requests, want_version):
|
||||||
"""Turn supplied AllocationCandidates object into a dict containing
|
"""Turn supplied AllocationCandidates object into a dict containing
|
||||||
allocation requests and provider summaries.
|
allocation requests and provider summaries.
|
||||||
|
|
||||||
@ -193,8 +205,11 @@ def _transform_allocation_candidates(alloc_cands, want_version):
|
|||||||
alloc_cands.allocation_requests)
|
alloc_cands.allocation_requests)
|
||||||
|
|
||||||
include_traits = want_version.matches((1, 17))
|
include_traits = want_version.matches((1, 17))
|
||||||
p_sums = _transform_provider_summaries(alloc_cands.provider_summaries,
|
include_all_resources = want_version.matches((1, 27))
|
||||||
include_traits=include_traits)
|
p_sums = _transform_provider_summaries(
|
||||||
|
alloc_cands.provider_summaries, requests,
|
||||||
|
include_traits=include_traits,
|
||||||
|
include_all_resources=include_all_resources)
|
||||||
return {
|
return {
|
||||||
'allocation_requests': a_reqs,
|
'allocation_requests': a_reqs,
|
||||||
'provider_summaries': p_sums,
|
'provider_summaries': p_sums,
|
||||||
@ -255,7 +270,7 @@ def list_allocation_candidates(req):
|
|||||||
raise webob.exc.HTTPBadRequest(six.text_type(exc))
|
raise webob.exc.HTTPBadRequest(six.text_type(exc))
|
||||||
|
|
||||||
response = req.response
|
response = req.response
|
||||||
trx_cands = _transform_allocation_candidates(cands, want_version)
|
trx_cands = _transform_allocation_candidates(cands, requests, want_version)
|
||||||
json_data = jsonutils.dumps(trx_cands)
|
json_data = jsonutils.dumps(trx_cands)
|
||||||
response.body = encodeutils.to_utf8(json_data)
|
response.body = encodeutils.to_utf8(json_data)
|
||||||
response.content_type = 'application/json'
|
response.content_type = 'application/json'
|
||||||
|
@ -70,6 +70,9 @@ VERSIONS = [
|
|||||||
# querystring groups in GET /allocation_candidates
|
# querystring groups in GET /allocation_candidates
|
||||||
'1.26', # Add ability to specify inventory with reserved value equal to
|
'1.26', # Add ability to specify inventory with reserved value equal to
|
||||||
# total.
|
# total.
|
||||||
|
'1.27', # Include all resource class inventories in `provider_summaries`
|
||||||
|
# field in response of `GET /allocation_candidates` API even if
|
||||||
|
# the resource class is not in the requested resources.
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -3684,45 +3684,16 @@ def _merge_candidates(candidates, group_policy=None):
|
|||||||
|
|
||||||
# Now we have to produce provider summaries. The provider summaries in
|
# Now we have to produce provider summaries. The provider summaries in
|
||||||
# the candidates input contain all the information; we just need to
|
# the candidates input contain all the information; we just need to
|
||||||
# filter it down to only the providers and resource classes* in our
|
# filter it down to only the providers in our merged list of allocation
|
||||||
# merged list of allocation requests.
|
# requests.
|
||||||
# *With blueprint placement-return-all-resources, all resource classes
|
rps_in_areq = set()
|
||||||
# should be included, so that condition will need to be removed either
|
|
||||||
# here or there, depending which lands first.
|
|
||||||
# To make this easier, first index all our allocation requests as a
|
|
||||||
# dict, keyed by resource provider UUID, of sets of resource class
|
|
||||||
# names.
|
|
||||||
rcs_by_rp = collections.defaultdict(set)
|
|
||||||
for areq in areqs:
|
for areq in areqs:
|
||||||
for arr in areq.resource_requests:
|
for arr in areq.resource_requests:
|
||||||
rcs_by_rp[arr.resource_provider.uuid].add(arr.resource_class)
|
rps_in_areq.add(arr.resource_provider.uuid)
|
||||||
# Now walk the input candidates' provider summaries, building a dict,
|
psums = [psum for psum in all_psums if
|
||||||
# keyed by resource provider UUID, of ProviderSummary representing
|
psum.resource_provider.uuid in rps_in_areq]
|
||||||
# that provider, and including any of its resource classes found in the
|
|
||||||
# index we built from our allocation requests above*.
|
|
||||||
# *See above.
|
|
||||||
psums_by_rp = {}
|
|
||||||
for psum in all_psums:
|
|
||||||
rp_uuid = psum.resource_provider.uuid
|
|
||||||
# If everything from this provider was filtered out, don't add an
|
|
||||||
# (empty) entry for it.
|
|
||||||
if rp_uuid not in rcs_by_rp:
|
|
||||||
continue
|
|
||||||
if rp_uuid not in psums_by_rp:
|
|
||||||
psums_by_rp[rp_uuid] = ProviderSummary(
|
|
||||||
resource_provider=psum.resource_provider, resources=[],
|
|
||||||
# Should always be the same; no need to check/update below.
|
|
||||||
traits=psum.traits)
|
|
||||||
# NOTE(efried): To subsume blueprint placement-return-all-resources
|
|
||||||
# replace this loop with:
|
|
||||||
# psums_by_rp[rp_uuid].resources = psum.resources
|
|
||||||
resources = set(psums_by_rp[rp_uuid].resources)
|
|
||||||
for psumres in psum.resources:
|
|
||||||
if psumres.resource_class in rcs_by_rp[rp_uuid]:
|
|
||||||
resources.add(psumres)
|
|
||||||
psums_by_rp[rp_uuid].resources = list(resources)
|
|
||||||
|
|
||||||
return areqs, list(psums_by_rp.values())
|
return areqs, psums
|
||||||
|
|
||||||
|
|
||||||
@base.VersionedObjectRegistry.register_if(False)
|
@base.VersionedObjectRegistry.register_if(False)
|
||||||
|
@ -329,3 +329,10 @@ non-sharing tree or associated via the specified aggregate(s).
|
|||||||
|
|
||||||
Starting with this version, it is allowed to set the reserved value of the
|
Starting with this version, it is allowed to set the reserved value of the
|
||||||
resource provider inventory to be equal to total.
|
resource provider inventory to be equal to total.
|
||||||
|
|
||||||
|
1.27 Include all resource class inventories in provider_summaries
|
||||||
|
-----------------------------------------------------------------
|
||||||
|
|
||||||
|
Include all resource class inventories in the ``provider_summaries`` field in
|
||||||
|
response of the ``GET /allocation_candidates`` API even if the resource class
|
||||||
|
is not in the requested resources.
|
||||||
|
@ -399,12 +399,8 @@ class AllocationCandidatesTestCase(tb.PlacementDbBaseTestCase):
|
|||||||
expected = {
|
expected = {
|
||||||
'cn1': set([
|
'cn1': set([
|
||||||
(fields.ResourceClass.VCPU, 8, 0),
|
(fields.ResourceClass.VCPU, 8, 0),
|
||||||
# TODO(tetsuro) - Bug#1760276: provider_summaries should
|
(fields.ResourceClass.MEMORY_MB, 2048, 0),
|
||||||
# include all the resources that the resource provider has,
|
(fields.ResourceClass.DISK_GB, 2000, 0)
|
||||||
# but currently it includes only resources that are requested.
|
|
||||||
#
|
|
||||||
# (fields.ResourceClass.MEMORY_MB, 2048, 0),
|
|
||||||
# (fields.ResourceClass.DISK_GB, 2000, 0),
|
|
||||||
]),
|
]),
|
||||||
}
|
}
|
||||||
self._validate_provider_summary_resources(expected, alloc_cands)
|
self._validate_provider_summary_resources(expected, alloc_cands)
|
||||||
@ -1104,6 +1100,7 @@ class AllocationCandidatesTestCase(tb.PlacementDbBaseTestCase):
|
|||||||
'cn': set([
|
'cn': set([
|
||||||
(fields.ResourceClass.VCPU, 24, 0),
|
(fields.ResourceClass.VCPU, 24, 0),
|
||||||
(fields.ResourceClass.MEMORY_MB, 2048, 0),
|
(fields.ResourceClass.MEMORY_MB, 2048, 0),
|
||||||
|
(fields.ResourceClass.DISK_GB, 1600, 0),
|
||||||
]),
|
]),
|
||||||
'ss': set([
|
'ss': set([
|
||||||
(fields.ResourceClass.DISK_GB, 2000, 0),
|
(fields.ResourceClass.DISK_GB, 2000, 0),
|
||||||
@ -1154,10 +1151,7 @@ class AllocationCandidatesTestCase(tb.PlacementDbBaseTestCase):
|
|||||||
'cn': set([
|
'cn': set([
|
||||||
(fields.ResourceClass.VCPU, 24, 0),
|
(fields.ResourceClass.VCPU, 24, 0),
|
||||||
(fields.ResourceClass.MEMORY_MB, 2048, 0),
|
(fields.ResourceClass.MEMORY_MB, 2048, 0),
|
||||||
# NOTE(efried): We don't (yet) populate provider summaries with
|
(fields.ResourceClass.DISK_GB, 1600, 0),
|
||||||
# provider resources that aren't part of the result. With
|
|
||||||
# blueprint placement-return-all-requests, uncomment this line:
|
|
||||||
# (fields.ResourceClass.DISK_GB, 1600, 0),
|
|
||||||
]),
|
]),
|
||||||
'ss': set([
|
'ss': set([
|
||||||
(fields.ResourceClass.DISK_GB, 1600, 0),
|
(fields.ResourceClass.DISK_GB, 1600, 0),
|
||||||
@ -1370,10 +1364,12 @@ class AllocationCandidatesTestCase(tb.PlacementDbBaseTestCase):
|
|||||||
|
|
||||||
expected = {
|
expected = {
|
||||||
'cn1': set([
|
'cn1': set([
|
||||||
(fields.ResourceClass.VCPU, 24, 0)
|
(fields.ResourceClass.VCPU, 24, 0),
|
||||||
|
(fields.ResourceClass.MEMORY_MB, 2048, 0),
|
||||||
]),
|
]),
|
||||||
'cn2': set([
|
'cn2': set([
|
||||||
(fields.ResourceClass.VCPU, 24, 0)
|
(fields.ResourceClass.VCPU, 24, 0),
|
||||||
|
(fields.ResourceClass.MEMORY_MB, 2048, 0),
|
||||||
]),
|
]),
|
||||||
'ss1': set([
|
'ss1': set([
|
||||||
(fields.ResourceClass.DISK_GB, 1600, 0),
|
(fields.ResourceClass.DISK_GB, 1600, 0),
|
||||||
@ -1434,9 +1430,11 @@ class AllocationCandidatesTestCase(tb.PlacementDbBaseTestCase):
|
|||||||
expected = {
|
expected = {
|
||||||
'cn1': set([
|
'cn1': set([
|
||||||
(fields.ResourceClass.VCPU, 24, 0),
|
(fields.ResourceClass.VCPU, 24, 0),
|
||||||
|
(fields.ResourceClass.MEMORY_MB, 2048, 0),
|
||||||
]),
|
]),
|
||||||
'cn2': set([
|
'cn2': set([
|
||||||
(fields.ResourceClass.VCPU, 24, 0),
|
(fields.ResourceClass.VCPU, 24, 0),
|
||||||
|
(fields.ResourceClass.MEMORY_MB, 2048, 0),
|
||||||
]),
|
]),
|
||||||
'ss1': set([
|
'ss1': set([
|
||||||
(fields.ResourceClass.DISK_GB, 1600, 0),
|
(fields.ResourceClass.DISK_GB, 1600, 0),
|
||||||
|
@ -277,13 +277,51 @@ tests:
|
|||||||
$.allocation_requests.`len`: 0
|
$.allocation_requests.`len`: 0
|
||||||
$.provider_summaries.`len`: 0
|
$.provider_summaries.`len`: 0
|
||||||
|
|
||||||
# Bug#1760276: provider_summaries should include all the resources that the
|
# Before microversion 1.27, the ``provider_summaries`` field in the response
|
||||||
# resource provider has, ut currently it includes only resources that are requested.
|
# of the ``GET /allocation_candidates`` API included inventories of resource
|
||||||
|
# classes that are requested.
|
||||||
- name: get allocation candidates provider summaries with requested resource
|
- name: get allocation candidates provider summaries with requested resource
|
||||||
GET: /allocation_candidates?resources=VCPU:1
|
GET: /allocation_candidates?resources=VCPU:1
|
||||||
status: 200
|
status: 200
|
||||||
|
request_headers:
|
||||||
|
openstack-api-version: placement 1.26
|
||||||
response_json_paths:
|
response_json_paths:
|
||||||
$.allocation_requests.`len`: 2
|
$.allocation_requests.`len`: 2
|
||||||
$.provider_summaries.`len`: 2
|
$.provider_summaries.`len`: 2
|
||||||
$.provider_summaries["$ENVIRON['CN1_UUID']"].resources.`len`: 1
|
$.provider_summaries["$ENVIRON['CN1_UUID']"].resources.`len`: 1
|
||||||
|
$.provider_summaries["$ENVIRON['CN1_UUID']"].resources:
|
||||||
|
VCPU:
|
||||||
|
capacity: 384 # 16.0 * 24
|
||||||
|
used: 0
|
||||||
$.provider_summaries["$ENVIRON['CN2_UUID']"].resources.`len`: 1
|
$.provider_summaries["$ENVIRON['CN2_UUID']"].resources.`len`: 1
|
||||||
|
$.provider_summaries["$ENVIRON['CN2_UUID']"].resources:
|
||||||
|
VCPU:
|
||||||
|
capacity: 384 # 16.0 * 24
|
||||||
|
used: 0
|
||||||
|
|
||||||
|
# From microversion 1.27, the ``provider_summaries`` field includes
|
||||||
|
# all the resource class inventories regardless of whether it is requested.
|
||||||
|
- name: get allocation candidates provider summaries with all resources
|
||||||
|
GET: /allocation_candidates?resources=VCPU:1
|
||||||
|
status: 200
|
||||||
|
request_headers:
|
||||||
|
openstack-api-version: placement 1.27
|
||||||
|
response_json_paths:
|
||||||
|
$.allocation_requests.`len`: 2
|
||||||
|
$.provider_summaries.`len`: 2
|
||||||
|
$.provider_summaries["$ENVIRON['CN1_UUID']"].resources.`len`: 2
|
||||||
|
$.provider_summaries["$ENVIRON['CN1_UUID']"].resources:
|
||||||
|
VCPU:
|
||||||
|
capacity: 384 # 16.0 * 24
|
||||||
|
used: 0
|
||||||
|
MEMORY_MB:
|
||||||
|
capacity: 196608 # 1.5 * 128G
|
||||||
|
used: 0
|
||||||
|
$.provider_summaries["$ENVIRON['CN2_UUID']"].resources.`len`: 2
|
||||||
|
$.provider_summaries["$ENVIRON['CN2_UUID']"].resources:
|
||||||
|
VCPU:
|
||||||
|
capacity: 384 # 16.0 * 24
|
||||||
|
used: 0
|
||||||
|
MEMORY_MB:
|
||||||
|
capacity: 196608 # 1.5 * 128G
|
||||||
|
used: 0
|
||||||
|
@ -167,6 +167,9 @@ tests:
|
|||||||
MEMORY_MB:
|
MEMORY_MB:
|
||||||
capacity: 4096
|
capacity: 4096
|
||||||
used: 0
|
used: 0
|
||||||
|
DISK_GB:
|
||||||
|
capacity: 500
|
||||||
|
used: 0
|
||||||
$.provider_summaries["$ENVIRON['CN_MIDDLE']"].resources:
|
$.provider_summaries["$ENVIRON['CN_MIDDLE']"].resources:
|
||||||
VCPU:
|
VCPU:
|
||||||
capacity: 8
|
capacity: 8
|
||||||
|
@ -39,13 +39,13 @@ tests:
|
|||||||
response_json_paths:
|
response_json_paths:
|
||||||
$.errors[0].title: Not Acceptable
|
$.errors[0].title: Not Acceptable
|
||||||
|
|
||||||
- name: latest microversion is 1.26
|
- name: latest microversion is 1.27
|
||||||
GET: /
|
GET: /
|
||||||
request_headers:
|
request_headers:
|
||||||
openstack-api-version: placement latest
|
openstack-api-version: placement latest
|
||||||
response_headers:
|
response_headers:
|
||||||
vary: /openstack-api-version/
|
vary: /openstack-api-version/
|
||||||
openstack-api-version: placement 1.26
|
openstack-api-version: placement 1.27
|
||||||
|
|
||||||
- name: other accept header bad version
|
- name: other accept header bad version
|
||||||
GET: /
|
GET: /
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
From microversion 1.27, the ``provider_summaries`` field in the
|
||||||
|
response of the ``GET /allocation_candidates`` API includes all
|
||||||
|
the resource class inventories, while it had only requested
|
||||||
|
resource class inventories with older microversions.
|
||||||
|
Now callers can use this additional inventory information in
|
||||||
|
making further sorting or filtering decisions.
|
Loading…
Reference in New Issue
Block a user