Exemple #1
0
    def __init__(self, context, request, has_trees, sharing, suffix=''):
        """Initializes the object retrieving and caching matching providers
        for each conditions like resource and aggregates from database.

        :raises placement.exception.ResourceProviderNotFound if there is no
                provider found which satisfies the request.
        """
        # TODO(tetsuro): split this into smaller functions reordering
        self.context = context

        # The request group suffix
        self.suffix = suffix

        # A dict, keyed by resource class internal ID, of the amounts of that
        # resource class being requested by the group.
        self.resources = {
            rc_cache.RC_CACHE.id_from_string(key): value
            for key, value in request.resources.items()
        }

        # A list of lists of aggregate UUIDs that the providers matching for
        # that request group must be members of
        self.member_of = request.member_of

        # A list of aggregate UUIDs that the providers matching for
        # that request group must not be members of
        self.forbidden_aggs = request.forbidden_aggs

        # A set of provider ids that matches the requested positive aggregates
        self.rps_in_aggs = set()
        if self.member_of:
            self.rps_in_aggs = provider_ids_matching_aggregates(
                context, self.member_of)
            if not self.rps_in_aggs:
                raise exception.ResourceProviderNotFound()

        # If True, this RequestGroup represents requests which must be
        # satisfied by a single resource provider.  If False, represents a
        # request for resources in any resource provider in the same tree,
        # or a sharing provider.
        self.use_same_provider = request.use_same_provider

        # maps the trait name to the trait internal ID
        self.required_trait_map = {}
        self.forbidden_trait_map = {}
        for trait_map, traits in ((self.required_trait_map,
                                   request.required_traits),
                                  (self.forbidden_trait_map,
                                   request.forbidden_traits)):
            if traits:
                trait_map.update(trait_obj.ids_from_names(context, traits))

        # Internal id of a root provider. If provided, this RequestGroup must
        # be satisfied by resource provider(s) under the root provider.
        self.tree_root_id = None
        if request.in_tree:
            tree_ids = provider_ids_from_uuid(context, request.in_tree)
            if tree_ids is None:
                raise exception.ResourceProviderNotFound()
            self.tree_root_id = tree_ids.root_id
            LOG.debug(
                "getting allocation candidates in the same tree "
                "with the root provider %s", tree_ids.root_uuid)

        self._rps_with_resource = {}
        for rc_id, amount in self.resources.items():
            # NOTE(tetsuro): We could pass rps in requested aggregates to
            # get_providers_with_resource here once we explicitly put
            # aggregates to nested (non-root) providers (the aggregate
            # flows down feature) rather than applying later the implicit rule
            # that aggregate on root spans the whole tree
            provs_with_resource = get_providers_with_resource(
                context, rc_id, amount, tree_root_id=self.tree_root_id)
            if not provs_with_resource:
                raise exception.ResourceProviderNotFound()
            self._rps_with_resource[rc_id] = provs_with_resource

        # a set of resource provider IDs that share some inventory for some
        # resource class.
        self._sharing_providers = sharing

        # bool indicating there is some level of nesting in the environment
        self.has_trees = has_trees
def _get_all_by_filters_from_db(context, filters):
    # Eg. filters can be:
    #  filters = {
    #      'name': <name>,
    #      'uuid': <uuid>,
    #      'member_of': [[<aggregate_uuid>, <aggregate_uuid>],
    #                    [<aggregate_uuid>]]
    #      'forbidden_aggs': [<aggregate_uuid>, <aggregate_uuid>]
    #      'resources': {
    #          'VCPU': 1,
    #          'MEMORY_MB': 1024
    #      },
    #      'in_tree': <uuid>,
    #      'required': [<trait_name>, ...]
    #  }
    if not filters:
        filters = {}
    else:
        # Since we modify the filters, copy them so that we don't modify
        # them in the calling program.
        filters = copy.deepcopy(filters)
    name = filters.pop('name', None)
    uuid = filters.pop('uuid', None)
    member_of = filters.pop('member_of', [])
    forbidden_aggs = filters.pop('forbidden_aggs', [])
    required = set(filters.pop('required', []))
    forbidden = set([trait for trait in required
                     if trait.startswith('!')])
    required = required - forbidden
    forbidden = set([trait.lstrip('!') for trait in forbidden])
    resources = filters.pop('resources', {})
    in_tree = filters.pop('in_tree', None)

    rp = sa.alias(_RP_TBL, name="rp")
    root_rp = sa.alias(_RP_TBL, name="root_rp")
    parent_rp = sa.alias(_RP_TBL, name="parent_rp")

    cols = [
        rp.c.id,
        rp.c.uuid,
        rp.c.name,
        rp.c.generation,
        rp.c.updated_at,
        rp.c.created_at,
        root_rp.c.uuid.label("root_provider_uuid"),
        parent_rp.c.uuid.label("parent_provider_uuid"),
    ]

    rp_to_root = sa.join(
        rp, root_rp,
        rp.c.root_provider_id == root_rp.c.id)
    rp_to_parent = sa.outerjoin(
        rp_to_root, parent_rp,
        rp.c.parent_provider_id == parent_rp.c.id)

    query = sa.select(cols).select_from(rp_to_parent)

    if name:
        query = query.where(rp.c.name == name)
    if uuid:
        query = query.where(rp.c.uuid == uuid)
    if in_tree:
        # The 'in_tree' parameter is the UUID of a resource provider that
        # the caller wants to limit the returned providers to only those
        # within its "provider tree". So, we look up the resource provider
        # having the UUID specified by the 'in_tree' parameter and grab the
        # root_provider_id value of that record. We can then ask for only
        # those resource providers having a root_provider_id of that value.
        tree_ids = res_ctx.provider_ids_from_uuid(context, in_tree)
        if tree_ids is None:
            # List operations should simply return an empty list when a
            # non-existing resource provider UUID is given.
            return []
        root_id = tree_ids.root_id
        query = query.where(rp.c.root_provider_id == root_id)
    if required:
        trait_map = trait_obj.ids_from_names(context, required)
        trait_rps = res_ctx._get_provider_ids_having_all_traits(
            context, trait_map)
        if not trait_rps:
            return []
        query = query.where(rp.c.id.in_(trait_rps))
    if forbidden:
        trait_map = trait_obj.ids_from_names(context, forbidden)
        trait_rps = res_ctx.get_provider_ids_having_any_trait(
            context, trait_map)
        if trait_rps:
            query = query.where(~rp.c.id.in_(trait_rps))
    if member_of:
        rps_in_aggs = res_ctx.provider_ids_matching_aggregates(
            context, member_of)
        if not rps_in_aggs:
            return []
        query = query.where(rp.c.id.in_(rps_in_aggs))
    if forbidden_aggs:
        rps_bad_aggs = res_ctx.provider_ids_matching_aggregates(
            context, [forbidden_aggs])
        if rps_bad_aggs:
            query = query.where(~rp.c.id.in_(rps_bad_aggs))
    for rc_name, amount in resources.items():
        rc_id = context.rc_cache.id_from_string(rc_name)
        rps_with_resource = res_ctx.get_providers_with_resource(
            context, rc_id, amount)
        rps_with_resource = (rp[0] for rp in rps_with_resource)
        query = query.where(rp.c.id.in_(rps_with_resource))

    return context.session.execute(query).fetchall()
    def _get_by_one_request(context, request, sharing_providers, has_trees):
        """Get allocation candidates for one RequestGroup.

        Must be called from within an placement_context_manager.reader
        (or writer) context.

        :param context: Nova RequestContext.
        :param request: One placement.lib.RequestGroup
        :param sharing_providers: dict, keyed by resource class internal ID, of
                                  the set of provider IDs containing shared
                                  inventory of that resource class
        :param has_trees: bool indicating there is some level of nesting in the
                          environment (if there isn't, we take faster, simpler
                          code paths)
        :return: A tuple of (allocation_requests, provider_summaries)
                 satisfying `request`.
        """
        # Transform resource string names to internal integer IDs
        resources = {
            rc_cache.RC_CACHE.id_from_string(key): value
            for key, value in request.resources.items()
        }

        # maps the trait name to the trait internal ID
        required_trait_map = {}
        forbidden_trait_map = {}
        for trait_map, traits in (
                (required_trait_map, request.required_traits),
                (forbidden_trait_map, request.forbidden_traits)):
            if traits:
                trait_map.update(trait_obj.ids_from_names(context, traits))

        member_of = request.member_of
        tree_root_id = None
        if request.in_tree:
            tree_ids = rp_obj.provider_ids_from_uuid(context, request.in_tree)
            if tree_ids is None:
                # List operations should simply return an empty list when a
                # non-existing resource provider UUID is given for in_tree.
                return [], []
            tree_root_id = tree_ids.root_id
            LOG.debug("getting allocation candidates in the same tree "
                      "with the root provider %s", tree_ids.root_uuid)

        any_sharing = any(sharing_providers.values())
        if not request.use_same_provider and (has_trees or any_sharing):
            # TODO(jaypipes): The check/callout to handle trees goes here.
            # Build a dict, keyed by resource class internal ID, of lists of
            # internal IDs of resource providers that share some inventory for
            # each resource class requested.
            # If there aren't any providers that have any of the
            # required traits, just exit early...
            if required_trait_map:
                # TODO(cdent): Now that there is also a forbidden_trait_map
                # it should be possible to further optimize this attempt at
                # a quick return, but we leave that to future patches for
                # now.
                trait_rps = rp_obj.get_provider_ids_having_any_trait(
                    context, required_trait_map)
                if not trait_rps:
                    return [], []
            rp_candidates = rp_obj.get_trees_matching_all(
                context, resources, required_trait_map, forbidden_trait_map,
                sharing_providers, member_of, tree_root_id)
            return _alloc_candidates_multiple_providers(
                context, resources, required_trait_map, forbidden_trait_map,
                rp_candidates)

        # Either we are processing a single-RP request group, or there are no
        # sharing providers that (help) satisfy the request.  Get a list of
        # tuples of (internal provider ID, root provider ID) that have ALL
        # the requested resources and more efficiently construct the
        # allocation requests.
        rp_tuples = rp_obj.get_provider_ids_matching(
            context, resources, required_trait_map, forbidden_trait_map,
            member_of, tree_root_id)
        return _alloc_candidates_single_provider(context, resources, rp_tuples)