Ejemplo n.º 1
0
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())
Ejemplo n.º 2
0
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())