Пример #1
0
    def id_from_string(self, rc_str):
        """Given a string representation of a resource class -- e.g. "DISK_GB"
        or "IRON_SILVER" -- return the integer code for the resource class. For
        standard resource classes, this integer code will match the list of
        resource classes on the fields.ResourceClass field type. Other custom
        resource classes will cause a DB lookup into the resource_classes
        table, however the results of these DB lookups are cached since the
        lookups are so frequent.

        :param rc_str: The string representation of the resource class to look
                       up a numeric identifier for.
        :returns integer identifier for the resource class, or None, if no such
                 resource class was found in the list of standard resource
                 classes or the resource_classes database table.
        :raises `exception.ResourceClassNotFound` if rc_str cannot be found in
                either the standard classes or the DB.
        """
        # First check the standard resource classes
        if rc_str in fields.ResourceClass.STANDARD:
            return fields.ResourceClass.STANDARD.index(rc_str)

        with lockutils.lock(_LOCKNAME):
            if rc_str in self.id_cache:
                return self.id_cache[rc_str]
            # Otherwise, check the database table
            _refresh_from_db(self.ctx, self)
            if rc_str in self.id_cache:
                return self.id_cache[rc_str]
            raise exception.ResourceClassNotFound(resource_class=rc_str)
Пример #2
0
    def string_from_id(self, rc_id):
        """The reverse of the id_from_string() method. Given a supplied numeric
        identifier for a resource class, we look up the corresponding string
        representation, either in the list of standard resource classes or via
        a DB lookup. The results of these DB lookups are cached since the
        lookups are so frequent.

        :param rc_id: The numeric representation of the resource class to look
                      up a string identifier for.
        :returns: string identifier for the resource class, or None, if no such
                 resource class was found in the list of standard resource
                 classes or the resource_classes database table.
        :raises `exception.ResourceClassNotFound` if rc_id cannot be found in
                either the standard classes or the DB.
        """
        # First check the fields.ResourceClass.STANDARD values
        try:
            return fields.ResourceClass.STANDARD[rc_id]
        except IndexError:
            pass

        with lockutils.lock(_LOCKNAME):
            if rc_id in self.str_cache:
                return self.str_cache[rc_id]

            # Otherwise, check the database table
            _refresh_from_db(self.ctx, self)
            if rc_id in self.str_cache:
                return self.str_cache[rc_id]
            raise exception.ResourceClassNotFound(resource_class=rc_id)
Пример #3
0
    def all_from_string(self, rc_str):
        """Given a string representation of a resource class -- e.g. "DISK_GB"
        or "CUSTOM_IRON_SILVER" -- return all the resource class info.

        :param rc_str: The string representation of the resource class for
                       which to look up a resource_class.
        :returns: dict representing the resource class fields, if the
                  resource class was found in the list of standard
                  resource classes or the resource_classes database table.
        :raises: `exception.ResourceClassNotFound` if rc_str cannot be found in
                 either the standard classes or the DB.
        """
        # First check the standard resource classes
        if rc_str in fields.ResourceClass.STANDARD:
            return {
                'id': fields.ResourceClass.STANDARD.index(rc_str),
                'name': rc_str,
                'updated_at': None,
                'created_at': None
            }

        with lockutils.lock(_LOCKNAME):
            if rc_str in self.all_cache:
                return self.all_cache[rc_str]
            # Otherwise, check the database table
            _refresh_from_db(self.ctx, self)
            if rc_str in self.all_cache:
                return self.all_cache[rc_str]
            raise exception.ResourceClassNotFound(resource_class=rc_str)
Пример #4
0
def validate_resources(ctx, rc_names):
    """Ensure that all the resource classes requested are valid."""
    query = """
            MATCH (rc:RESOURCE_CLASS)
            RETURN DISTINCT rc.name AS rc_name
    """
    result = ctx.tx.run(query).data()
    valid_names = set([rec["rc_name"] for rec in result])
    invalid_rc_names = set(rc_names) - valid_names
    if invalid_rc_names:
        resource_class = ",".join(invalid_rc_names)
        raise exception.ResourceClassNotFound(resource_class)
Пример #5
0
    def get_by_name(cls, context, name):
        """Return a ResourceClass object with the given string name.

        :param name: String name of the resource class to find

        :raises: ResourceClassNotFound if no such resource class was found
        """
        query = """
                MATCH (rc:RESOURCE_CLASS {name: '%s'})
                RETURN rc
        """ % name
        result = context.tx.run(query).data()
        if not result:
            raise exception.ResourceClassNotFound(resource_class=name)
        rec = db.pythonize(result[0])
        obj = cls(context,
                  name=name,
                  updated_at=rec.get("updated_at"),
                  created_at=rec.get("created_at"))
        return obj
Пример #6
0
    def all_from_string(self, rc_str):
        """Given a string representation of a resource class -- e.g. "DISK_GB"
        or "CUSTOM_IRON_SILVER" -- return all the resource class info.

        :param rc_str: The string representation of the resource class for
                       which to look up a resource_class.
        :returns: dict representing the resource class fields, if the
                  resource class was found in the resource_classes database
                  table.
        :raises: `exception.ResourceClassNotFound` if rc_str cannot be found in
                 the DB.
        """
        rc_id_str = self.all_cache.get(rc_str)
        if rc_id_str is not None:
            return rc_id_str

        # Otherwise, check the database table
        with lockutils.lock(_LOCKNAME):
            _refresh_from_db(self.ctx, self)
            if rc_str in self.all_cache:
                return self.all_cache[rc_str]
            raise exception.ResourceClassNotFound(resource_class=rc_str)
Пример #7
0
    def string_from_id(self, rc_id):
        """The reverse of the id_from_string() method. Given a supplied numeric
        identifier for a resource class, we look up the corresponding string
        representation, via a DB lookup. The results of these DB lookups are
        cached since the lookups are so frequent.

        :param rc_id: The numeric representation of the resource class to look
                      up a string identifier for.
        :returns: String identifier for the resource class.
        :raises `exception.ResourceClassNotFound` if rc_id cannot be found in
                the DB.
        """
        rc_str = self.str_cache.get(rc_id)
        if rc_str is not None:
            return rc_str

        # Otherwise, check the database table
        with lockutils.lock(_LOCKNAME):
            _refresh_from_db(self.ctx, self)
            if rc_id in self.str_cache:
                return self.str_cache[rc_id]
            raise exception.ResourceClassNotFound(resource_class=rc_id)
Пример #8
0
    def id_from_string(self, rc_str):
        """Given a string representation of a resource class -- e.g. "DISK_GB"
        or "CUSTOM_IRON_SILVER" -- return the integer code for the resource
        class by doing a DB lookup into the resource_classes table; however,
        the results of these DB lookups are cached since the lookups are so
        frequent.

        :param rc_str: The string representation of the resource class to look
                       up a numeric identifier for.
        :returns Integer identifier for the resource class.
        :raises `exception.ResourceClassNotFound` if rc_str cannot be found in
                the DB.
        """
        rc_id = self.id_cache.get(rc_str)
        if rc_id is not None:
            return rc_id

        # Otherwise, check the database table
        with lockutils.lock(_LOCKNAME):
            _refresh_from_db(self.ctx, self)
            if rc_str in self.id_cache:
                return self.id_cache[rc_str]
            raise exception.ResourceClassNotFound(resource_class=rc_str)
Пример #9
0
def _check_capacity_exceeded(context, allocs):
    """Checks to see if the supplied allocation records would result in any of
    the inventories involved having their capacity exceeded.

    Raises an InvalidAllocationCapacityExceeded exception if any inventory
    would be exhausted by the allocation. Raises an
    InvalidAllocationConstraintsViolated exception if any of the `step_size`,
    `min_unit` or `max_unit` constraints in an inventory will be violated
    by any one of the allocations.

    If no inventories would be exceeded or violated by the allocations, the
    function returns a list of `ResourceProvider` objects that contain the
    generation at the time of the check.

    :param ctx: `placement.context.RequestContext` that has an oslo_db
                Session
    :param allocs: List of `Allocation` objects to check
    """
    rc_names = set([a.resource_class for a in allocs])
    # Make sure that all the rc_names are valid
    query = """
            MATCH (rc:RESOURCE_CLASS)
            WHERE rc.name IN {names}
            RETURN rc.name AS rc_name
    """
    result = context.tx.run(query, names=list(rc_names))
    db_names = set([rec["rc_name"] for rec in result])
    set_bad_names = rc_names - db_names
    if set_bad_names:
        bad_names = ",".join(set_bad_names)
        raise exception.ResourceClassNotFound(resource_class=bad_names)

    provider_uuids = set([a.resource_provider.uuid for a in allocs])
    query = """
            MATCH (rp:RESOURCE_PROVIDER)-[:PROVIDES]->(rc)
            WHERE rp.uuid IN {rp_uuids}
            AND labels(rc)[0] IN {labels}
            WITH rp, rc
            OPTIONAL MATCH p=(cs:CONSUMER)-[:USES]->(rc)
            WITH rp, rc, relationships(p)[0] AS allocs
            WITH rp, rc, sum(allocs.amount) AS total_usages
            RETURN rp, rc, labels(rc)[0] AS rc_name, total_usages
    """
    result = context.tx.run(query,
                            rp_uuids=list(provider_uuids),
                            labels=list(rc_names)).data()

    # Create a map keyed by (rp_uuid, res_class) for the records in the DB
    usage_map = {}
    provs_with_inv = set()
    for record in result:
        map_key = (record["rp"]["uuid"], record["rc_name"])
        if map_key in usage_map:
            raise KeyError("%s already in usage_map, bad query" % str(map_key))
        usage_map[map_key] = record
        provs_with_inv.add(record["rp"]["uuid"])
    # Ensure that all providers have existing inventory
    missing_provs = provider_uuids - provs_with_inv
    if missing_provs:
        class_str = ", ".join(rc_names)
        provider_str = ", ".join(missing_provs)
        raise exception.InvalidInventory(resource_class=class_str,
                                         resource_provider=provider_str)

    res_providers = {}
    rp_resource_class_sum = collections.defaultdict(
        lambda: collections.defaultdict(int))
    for alloc in allocs:
        rc_name = alloc.resource_class
        rp_uuid = alloc.resource_provider.uuid
        if rp_uuid not in res_providers:
            res_providers[rp_uuid] = alloc.resource_provider
        amount_needed = alloc.used
        # No use checking usage if we're not asking for anything
        if amount_needed == 0:
            continue
        rp_resource_class_sum[rp_uuid][rc_name] += amount_needed
        key = (rp_uuid, rc_name)
        try:
            usage_record = usage_map[key]
        except KeyError:
            # The resource class at rc_name is not in the usage map.
            raise exception.InvalidInventory(
                resource_class=alloc.resource_class, resource_provider=rp_uuid)
        rc_record = usage_record["rc"]
        allocation_ratio = rc_record["allocation_ratio"]
        min_unit = rc_record["min_unit"]
        max_unit = rc_record["max_unit"]
        step_size = rc_record["step_size"]
        total = rc_record["total"]
        reserved = rc_record["reserved"]

        # check min_unit, max_unit, step_size
        if ((amount_needed < min_unit) or (amount_needed > max_unit)
                or (amount_needed % step_size)):
            LOG.warning(
                "Allocation for %(rc)s on resource provider %(rp)s "
                "violates min_unit, max_unit, or step_size. "
                "Requested: %(requested)s, min_unit: %(min_unit)s, "
                "max_unit: %(max_unit)s, step_size: %(step_size)s", {
                    "rc": alloc.resource_class,
                    "rp": rp_uuid,
                    "requested": amount_needed,
                    "min_unit": min_unit,
                    "max_unit": max_unit,
                    "step_size": step_size
                })
            raise exception.InvalidAllocationConstraintsViolated(
                resource_class=alloc.resource_class, resource_provider=rp_uuid)

        # Should never be null, but just in case...
        used = usage_record["total_usages"] or 0
        capacity = (total - reserved) * allocation_ratio
        if (capacity < (used + amount_needed) or capacity <
            (used + rp_resource_class_sum[rp_uuid][rc_name])):
            LOG.warning(
                "Over capacity for %(rc)s on resource provider %(rp)s. "
                "Needed: %(needed)s, Used: %(used)s, Capacity: %(cap)s", {
                    "rc": alloc.resource_class,
                    "rp": rp_uuid,
                    "needed": amount_needed,
                    "used": used,
                    "cap": capacity
                })
            raise exception.InvalidAllocationCapacityExceeded(
                resource_class=alloc.resource_class, resource_provider=rp_uuid)
    return res_providers