예제 #1
0
    def limit_check(self, context, tenant_id, resources, values):
        """Check simple quota limits.

        For limits--those quotas for which there is no usage
        synchronization function--this method checks that a set of
        proposed values are permitted by the limit restriction.

        If any of the proposed values is over the defined quota, an
        OverQuota exception will be raised with the sorted list of the
        resources which are too high.  Otherwise, the method returns
        nothing.

        :param context: The request context, for access checks.
        :param tenant_id: The tenant_id to check the quota.
        :param resources: A dictionary of the registered resources.
        :param values: A dictionary of the values to check against the
                       quota.
        """

        # Ensure no value is less than zero
        unders = [key for key, val in values.items() if val < 0]
        if unders:
            raise exceptions.InvalidQuotaValue(unders=sorted(unders))

        # Get the applicable quotas
        quotas = self._get_quotas(context, tenant_id, resources)

        # Check the quotas and construct a list of the resources that
        # would be put over limit by the desired values
        overs = [
            key for key, val in values.items()
            if quotas[key] >= 0 and quotas[key] < val
        ]
        if overs:
            raise exceptions.OverQuota(overs=sorted(overs))
    def __init__(self, *args, **kwargs):
        super(GBPQuotaBase, self).__init__(*args, **kwargs)

        tenant_id = kwargs['tenant_id']
        ctx = context.Context(user_id=None, tenant_id=tenant_id)
        class_name = self.__class__.__name__
        resource = DB_CLASS_TO_RESOURCE_NAMES[class_name]
        d = {resource: quota.CountableResource(resource, None,
                                               "quota_" + resource)}
        resource_quota = QUOTA_DRIVER.get_tenant_quotas(ctx, d,
                                                        tenant_id)[resource]
        if resource_quota == -1:
            return
        count = self._get_collection_count(ctx, self.__class__)
        if count >= resource_quota:
            raise nexcp.OverQuota(overs=[resource])
예제 #3
0
    def make_reservation(self, context, tenant_id, resources, deltas, plugin):
        # Lock current reservation table
        # NOTE(salv-orlando): This routine uses DB write locks.
        # These locks are acquired by the count() method invoked on resources.
        # Please put your shotguns aside.
        # A non locking algorithm for handling reservation is feasible, however
        # it will require two database writes even in cases when there are not
        # concurrent reservations.
        # For this reason it might be advisable to handle contention using
        # this kind of locks and paying the cost of a write set certification
        # failure when a MySQL Galera cluster is employed. Also, this class of
        # locks should be ok to use when support for sending "hotspot" writes
        # to a single node will be available.
        requested_resources = deltas.keys()
        with db_api.autonested_transaction(context.session):
            # get_tenant_quotes needs in input a dictionary mapping resource
            # name to BaseResosurce instances so that the default quota can be
            # retrieved
            current_limits = self.get_tenant_quotas(
                context, resources, tenant_id)
            unlimited_resources = set([resource for (resource, limit) in
                                       current_limits.items() if limit < 0])
            # Do not even bother counting resources and calculating headroom
            # for resources with unlimited quota
            LOG.debug(("Resources %s have unlimited quota limit. It is not "
                       "required to calculated headroom "),
                      ",".join(unlimited_resources))
            requested_resources = (set(requested_resources) -
                                   unlimited_resources)
            # Gather current usage information
            # TODO(salv-orlando): calling count() for every resource triggers
            # multiple queries on quota usage. This should be improved, however
            # this is not an urgent matter as the REST API currently only
            # allows allocation of a resource at a time
            # NOTE: pass plugin too for compatibility with CountableResource
            # instances
            current_usages = dict(
                (resource, resources[resource].count(
                    context, plugin, tenant_id, resync_usage=False)) for
                resource in requested_resources)
            # Adjust for expired reservations. Apparently it is cheaper than
            # querying every time for active reservations and counting overall
            # quantity of resources reserved
            expired_deltas = quota_api.get_reservations_for_resources(
                context, tenant_id, requested_resources, expired=True)
            # Verify that the request can be accepted with current limits
            resources_over_limit = []
            for resource in requested_resources:
                expired_reservations = expired_deltas.get(resource, 0)
                total_usage = current_usages[resource] - expired_reservations
                res_headroom = current_limits[resource] - total_usage
                LOG.debug(("Attempting to reserve %(delta)d items for "
                           "resource %(resource)s. Total usage: %(total)d; "
                           "quota limit: %(limit)d; headroom:%(headroom)d"),
                          {'resource': resource,
                           'delta': deltas[resource],
                           'total': total_usage,
                           'limit': current_limits[resource],
                           'headroom': res_headroom})
                if res_headroom < deltas[resource]:
                    resources_over_limit.append(resource)
                if expired_reservations:
                    self._handle_expired_reservations(context, tenant_id)

            if resources_over_limit:
                raise exceptions.OverQuota(overs=sorted(resources_over_limit))
            # Success, store the reservation
            # TODO(salv-orlando): Make expiration time configurable
            return quota_api.create_reservation(
                context, tenant_id, deltas)