def _alloc_candidates_single_provider(rg_ctx, rp_tuples): """Returns a tuple of (allocation requests, provider summaries) for a supplied set of requested resource amounts and resource providers. The supplied resource providers have capacity to satisfy ALL of the resources in the requested resources as well as ALL required traits that were requested by the user. This is used in two circumstances: - To get results for a RequestGroup with use_same_provider=True. - As an optimization when no sharing providers satisfy any of the requested resources, and nested providers are not in play. In these scenarios, we can more efficiently build the list of AllocationRequest and ProviderSummary objects due to not having to determine requests across multiple providers. :param rg_ctx: RequestGroupSearchContext :param rp_tuples: List of two-tuples of (provider ID, root provider ID)s for providers that matched the requested resources """ if not rp_tuples: return [], [] # Get all root resource provider IDs. root_ids = set(p[1] for p in rp_tuples) # Grab usage summaries for each provider usages = _get_usages_by_provider_tree(rg_ctx.context, root_ids) # Get a dict, keyed by resource provider internal ID, of trait string names # that provider has associated with it prov_traits = trait_obj.get_traits_by_provider_tree( rg_ctx.context, root_ids) # Get a dict, keyed by resource provider internal ID, of ProviderSummary # objects for all providers summaries = _build_provider_summaries(rg_ctx.context, usages, prov_traits) # Next, build up a list of allocation requests. These allocation requests # are AllocationRequest objects, containing resource provider UUIDs, # resource class names and amounts to consume from that resource provider alloc_requests = [] for rp_id, root_id in rp_tuples: rp_summary = summaries[rp_id] req_obj = _allocation_request_for_provider( rg_ctx.context, rg_ctx.resources, rp_summary.resource_provider, suffix=rg_ctx.suffix) alloc_requests.append(req_obj) # If this is a sharing provider, we have to include an extra # AllocationRequest for every possible anchor. traits = rp_summary.traits if os_traits.MISC_SHARES_VIA_AGGREGATE in traits: anchors = set([ p[1] for p in res_ctx.anchors_for_sharing_providers( rg_ctx.context, [rp_summary.resource_provider.id]) ]) for anchor in anchors: # We already added self if anchor == rp_summary.resource_provider.root_provider_uuid: continue req_obj = copy.copy(req_obj) req_obj.anchor_root_provider_uuid = anchor alloc_requests.append(req_obj) return alloc_requests, list(summaries.values())
def _alloc_candidates_multiple_providers(rg_ctx, rp_candidates): """Returns a tuple of (allocation requests, provider summaries) for a supplied set of requested resource amounts and tuples of (rp_id, root_id, rc_id). The supplied resource provider trees have capacity to satisfy ALL of the resources in the requested resources as well as ALL required traits that were requested by the user. This is a code path to get results for a RequestGroup with use_same_provider=False. In this scenario, we are able to use multiple providers within the same provider tree including sharing providers to satisfy different resources involved in a single request group. :param rg_ctx: RequestGroupSearchContext. :param rp_candidates: RPCandidates object representing the providers that satisfy the request for resources. """ if not rp_candidates: return [], [] # Get all the root resource provider IDs. We should include the first # values of rp_tuples because while sharing providers are root providers, # they have their "anchor" providers for the second value. root_ids = rp_candidates.all_rps # Grab usage summaries for each provider in the trees usages = _get_usages_by_provider_tree(rg_ctx.context, root_ids) # Get a dict, keyed by resource provider internal ID, of trait string names # that provider has associated with it prov_traits = trait_obj.get_traits_by_provider_tree( rg_ctx.context, root_ids) # Get a dict, keyed by resource provider internal ID, of ProviderSummary # objects for all providers summaries = _build_provider_summaries(rg_ctx.context, usages, prov_traits) # Get a dict, keyed by root provider internal ID, of a dict, keyed by # resource class internal ID, of lists of AllocationRequestResource objects tree_dict = collections.defaultdict(lambda: collections.defaultdict(list)) for rp in rp_candidates.rps_info: rp_summary = summaries[rp.id] tree_dict[rp.root_id][rp.rc_id].append( AllocationRequestResource( resource_provider=rp_summary.resource_provider, resource_class=rc_cache.RC_CACHE.string_from_id(rp.rc_id), amount=rg_ctx.resources[rp.rc_id], suffix=rg_ctx.suffix)) # Next, build up a set of allocation requests. These allocation requests # are AllocationRequest objects, containing resource provider UUIDs, # resource class names and amounts to consume from that resource provider alloc_requests = set() # Let's look into each tree for root_id, alloc_dict in tree_dict.items(): # Get request_groups, which is a list of lists of # AllocationRequestResource(ARR) per requested resource class(rc). # For example, if we have the alloc_dict: # {rc1_id: [ARR(rc1, rp1), ARR(rc1, rp2)], # rc2_id: [ARR(rc2, rp1), ARR(rc2, rp2)], # rc3_id: [ARR(rc3, rp1)]} # then the request_groups would be something like # [[ARR(rc1, rp1), ARR(rc1, rp2)], # [ARR(rc2, rp1), ARR(rc2, rp2)], # [ARR(rc3, rp1)]] # , which should be ordered by the resource class id. request_groups = [val for key, val in sorted(alloc_dict.items())] root_summary = summaries[root_id] root_uuid = root_summary.resource_provider.uuid root_alloc_reqs = set() # Using itertools.product, we get all the combinations of resource # providers in a tree. # For example, the sample in the comment above becomes: # [(ARR(rc1, ss1), ARR(rc2, ss1), ARR(rc3, ss1)), # (ARR(rc1, ss1), ARR(rc2, ss2), ARR(rc3, ss1)), # (ARR(rc1, ss2), ARR(rc2, ss1), ARR(rc3, ss1)), # (ARR(rc1, ss2), ARR(rc2, ss2), ARR(rc3, ss1))] for res_requests in itertools.product(*request_groups): if not _check_traits_for_alloc_request(res_requests, summaries, rg_ctx.required_trait_map, rg_ctx.forbidden_trait_map): # This combination doesn't satisfy trait constraints continue root_alloc_reqs.add( AllocationRequest(resource_requests=list(res_requests), anchor_root_provider_uuid=root_uuid)) alloc_requests |= root_alloc_reqs return list(alloc_requests), list(summaries.values())