diff --git a/nova/api/openstack/placement/handlers/allocation_candidate.py b/nova/api/openstack/placement/handlers/allocation_candidate.py index 80db5254e627..a4b916e540c6 100644 --- a/nova/api/openstack/placement/handlers/allocation_candidate.py +++ b/nova/api/openstack/placement/handlers/allocation_candidate.py @@ -116,10 +116,14 @@ def _transform_allocation_requests_list(alloc_reqs): 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 - resource provider UUID, of dicts of provider and inventory information. The - traits only show up when `include_traits` is `True`. + resource provider UUID, of dicts of provider and inventory information. + 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: { @@ -158,13 +162,21 @@ def _transform_provider_summaries(p_sums, include_traits=False): """ 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: resources = { psr.resource_class: { 'capacity': psr.capacity, '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} @@ -176,7 +188,7 @@ def _transform_provider_summaries(p_sums, include_traits=False): 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 allocation requests and provider summaries. @@ -193,8 +205,11 @@ def _transform_allocation_candidates(alloc_cands, want_version): alloc_cands.allocation_requests) include_traits = want_version.matches((1, 17)) - p_sums = _transform_provider_summaries(alloc_cands.provider_summaries, - include_traits=include_traits) + include_all_resources = want_version.matches((1, 27)) + p_sums = _transform_provider_summaries( + alloc_cands.provider_summaries, requests, + include_traits=include_traits, + include_all_resources=include_all_resources) return { 'allocation_requests': a_reqs, 'provider_summaries': p_sums, @@ -255,7 +270,7 @@ def list_allocation_candidates(req): raise webob.exc.HTTPBadRequest(six.text_type(exc)) 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) response.body = encodeutils.to_utf8(json_data) response.content_type = 'application/json' diff --git a/nova/api/openstack/placement/microversion.py b/nova/api/openstack/placement/microversion.py index e34ce0b7b4ce..c95caa8a1871 100644 --- a/nova/api/openstack/placement/microversion.py +++ b/nova/api/openstack/placement/microversion.py @@ -70,6 +70,9 @@ VERSIONS = [ # querystring groups in GET /allocation_candidates '1.26', # Add ability to specify inventory with reserved value equal to # 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. ] diff --git a/nova/api/openstack/placement/objects/resource_provider.py b/nova/api/openstack/placement/objects/resource_provider.py index 797a11f7e4f1..dffcdcedee1b 100644 --- a/nova/api/openstack/placement/objects/resource_provider.py +++ b/nova/api/openstack/placement/objects/resource_provider.py @@ -3684,45 +3684,16 @@ def _merge_candidates(candidates, group_policy=None): # Now we have to produce provider summaries. The provider summaries in # the candidates input contain all the information; we just need to - # filter it down to only the providers and resource classes* in our - # merged list of allocation requests. - # *With blueprint placement-return-all-resources, all resource classes - # 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) + # filter it down to only the providers in our merged list of allocation + # requests. + rps_in_areq = set() for areq in areqs: for arr in areq.resource_requests: - rcs_by_rp[arr.resource_provider.uuid].add(arr.resource_class) - # Now walk the input candidates' provider summaries, building a dict, - # keyed by resource provider UUID, of ProviderSummary representing - # 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) + rps_in_areq.add(arr.resource_provider.uuid) + psums = [psum for psum in all_psums if + psum.resource_provider.uuid in rps_in_areq] - return areqs, list(psums_by_rp.values()) + return areqs, psums @base.VersionedObjectRegistry.register_if(False) diff --git a/nova/api/openstack/placement/rest_api_version_history.rst b/nova/api/openstack/placement/rest_api_version_history.rst index 2673b8a9f20f..e5387749344f 100644 --- a/nova/api/openstack/placement/rest_api_version_history.rst +++ b/nova/api/openstack/placement/rest_api_version_history.rst @@ -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 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. diff --git a/nova/tests/functional/api/openstack/placement/db/test_allocation_candidates.py b/nova/tests/functional/api/openstack/placement/db/test_allocation_candidates.py index fe2da108998d..7de23904d45b 100644 --- a/nova/tests/functional/api/openstack/placement/db/test_allocation_candidates.py +++ b/nova/tests/functional/api/openstack/placement/db/test_allocation_candidates.py @@ -399,12 +399,8 @@ class AllocationCandidatesTestCase(tb.PlacementDbBaseTestCase): expected = { 'cn1': set([ (fields.ResourceClass.VCPU, 8, 0), - # TODO(tetsuro) - Bug#1760276: provider_summaries should - # include all the resources that the resource provider has, - # but currently it includes only resources that are requested. - # - # (fields.ResourceClass.MEMORY_MB, 2048, 0), - # (fields.ResourceClass.DISK_GB, 2000, 0), + (fields.ResourceClass.MEMORY_MB, 2048, 0), + (fields.ResourceClass.DISK_GB, 2000, 0) ]), } self._validate_provider_summary_resources(expected, alloc_cands) @@ -1104,6 +1100,7 @@ class AllocationCandidatesTestCase(tb.PlacementDbBaseTestCase): 'cn': set([ (fields.ResourceClass.VCPU, 24, 0), (fields.ResourceClass.MEMORY_MB, 2048, 0), + (fields.ResourceClass.DISK_GB, 1600, 0), ]), 'ss': set([ (fields.ResourceClass.DISK_GB, 2000, 0), @@ -1154,10 +1151,7 @@ class AllocationCandidatesTestCase(tb.PlacementDbBaseTestCase): 'cn': set([ (fields.ResourceClass.VCPU, 24, 0), (fields.ResourceClass.MEMORY_MB, 2048, 0), - # NOTE(efried): We don't (yet) populate provider summaries with - # provider resources that aren't part of the result. With - # blueprint placement-return-all-requests, uncomment this line: - # (fields.ResourceClass.DISK_GB, 1600, 0), + (fields.ResourceClass.DISK_GB, 1600, 0), ]), 'ss': set([ (fields.ResourceClass.DISK_GB, 1600, 0), @@ -1370,10 +1364,12 @@ class AllocationCandidatesTestCase(tb.PlacementDbBaseTestCase): expected = { 'cn1': set([ - (fields.ResourceClass.VCPU, 24, 0) + (fields.ResourceClass.VCPU, 24, 0), + (fields.ResourceClass.MEMORY_MB, 2048, 0), ]), 'cn2': set([ - (fields.ResourceClass.VCPU, 24, 0) + (fields.ResourceClass.VCPU, 24, 0), + (fields.ResourceClass.MEMORY_MB, 2048, 0), ]), 'ss1': set([ (fields.ResourceClass.DISK_GB, 1600, 0), @@ -1434,9 +1430,11 @@ class AllocationCandidatesTestCase(tb.PlacementDbBaseTestCase): expected = { 'cn1': set([ (fields.ResourceClass.VCPU, 24, 0), + (fields.ResourceClass.MEMORY_MB, 2048, 0), ]), 'cn2': set([ (fields.ResourceClass.VCPU, 24, 0), + (fields.ResourceClass.MEMORY_MB, 2048, 0), ]), 'ss1': set([ (fields.ResourceClass.DISK_GB, 1600, 0), diff --git a/nova/tests/functional/api/openstack/placement/gabbits/allocation-candidates.yaml b/nova/tests/functional/api/openstack/placement/gabbits/allocation-candidates.yaml index f9f700a4fed7..495342e8f9be 100644 --- a/nova/tests/functional/api/openstack/placement/gabbits/allocation-candidates.yaml +++ b/nova/tests/functional/api/openstack/placement/gabbits/allocation-candidates.yaml @@ -277,13 +277,51 @@ tests: $.allocation_requests.`len`: 0 $.provider_summaries.`len`: 0 -# Bug#1760276: provider_summaries should include all the resources that the -# resource provider has, ut currently it includes only resources that are requested. +# Before microversion 1.27, the ``provider_summaries`` field in the response +# of the ``GET /allocation_candidates`` API included inventories of resource +# classes that are requested. - name: get allocation candidates provider summaries with requested resource GET: /allocation_candidates?resources=VCPU:1 status: 200 + request_headers: + openstack-api-version: placement 1.26 response_json_paths: $.allocation_requests.`len`: 2 $.provider_summaries.`len`: 2 $.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: + 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 diff --git a/nova/tests/functional/api/openstack/placement/gabbits/granular.yaml b/nova/tests/functional/api/openstack/placement/gabbits/granular.yaml index 38a7413379f9..dc5f9f74c258 100644 --- a/nova/tests/functional/api/openstack/placement/gabbits/granular.yaml +++ b/nova/tests/functional/api/openstack/placement/gabbits/granular.yaml @@ -167,6 +167,9 @@ tests: MEMORY_MB: capacity: 4096 used: 0 + DISK_GB: + capacity: 500 + used: 0 $.provider_summaries["$ENVIRON['CN_MIDDLE']"].resources: VCPU: capacity: 8 diff --git a/nova/tests/functional/api/openstack/placement/gabbits/microversion.yaml b/nova/tests/functional/api/openstack/placement/gabbits/microversion.yaml index 1ffd631f5cda..83df248c1e57 100644 --- a/nova/tests/functional/api/openstack/placement/gabbits/microversion.yaml +++ b/nova/tests/functional/api/openstack/placement/gabbits/microversion.yaml @@ -39,13 +39,13 @@ tests: response_json_paths: $.errors[0].title: Not Acceptable -- name: latest microversion is 1.26 +- name: latest microversion is 1.27 GET: / request_headers: openstack-api-version: placement latest response_headers: vary: /openstack-api-version/ - openstack-api-version: placement 1.26 + openstack-api-version: placement 1.27 - name: other accept header bad version GET: / diff --git a/releasenotes/notes/placement-return-all-resources-bfc7e3f8b5151e28.yaml b/releasenotes/notes/placement-return-all-resources-bfc7e3f8b5151e28.yaml new file mode 100644 index 000000000000..ba3bb32afd2f --- /dev/null +++ b/releasenotes/notes/placement-return-all-resources-bfc7e3f8b5151e28.yaml @@ -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.