Example #1
0
    def test__remove_expired_reservations(self):
        for project, resource in itertools.product(self.projects,
                                                   self.resources):
            deltas = {resource: 1}
            with db_api.CONTEXT_WRITER.using(self.context):
                quota_api.create_reservation(self.context, project, deltas)

        # Initial check: the reservations are correctly created.
        for project in self.projects:
            for res in quota_obj.Reservation.get_objects(self.context,
                                                         project_id=project):
                self.assertEqual(1, len(res.resource_deltas))
                delta = res.resource_deltas[0]
                self.assertEqual(1, delta.amount)
                self.assertIn(delta.resource, self.resources)

        # Delete the expired reservations and check.
        # NOTE(ralonsoh): the timeout is set to -121 to force the deletion
        # of all created reservations, including those ones created in this
        # test. The value of 121 overcomes the 120 seconds of default
        # expiration time a reservation has.
        timeout = quota_api.RESERVATION_EXPIRATION_TIMEOUT
        quota_api.RESERVATION_EXPIRATION_TIMEOUT = -(timeout + 1)
        self.addCleanup(self._cleanup_timeout, timeout)
        self.quota_driver._remove_expired_reservations()
        res = quota_obj.Reservation.get_objects(self.context)
        self.assertEqual([], res)
Example #2
0
    def make_reservation(self, context, project_id, resources, deltas, plugin):
        resources_over_limit = []
        with db_api.CONTEXT_WRITER.using(context):
            # Filter out unlimited resources.
            limits = self.get_tenant_quotas(context, resources, project_id)
            unlimited_resources = set([
                resource for (resource, limit) in limits.items() if limit < 0
            ])
            requested_resources = (set(deltas.keys()) - unlimited_resources)

            # Delete expired reservations before counting valid ones. This
            # operation is fast and by calling it before making any
            # reservation, we ensure the freshness of the reservations.
            quota_api.remove_expired_reservations(context,
                                                  tenant_id=project_id)

            # Count the number of (1) used and (2) reserved resources for this
            # project_id. If any resource limit is exceeded, raise exception.
            for resource_name in requested_resources:
                tracked_resource = resources.get(resource_name)
                if not tracked_resource:
                    continue

                used_and_reserved = tracked_resource.count(
                    context, None, project_id, count_db_registers=True)
                resource_num = deltas[resource_name]
                if limits[resource_name] < (used_and_reserved + resource_num):
                    resources_over_limit.append(resource_name)

            if resources_over_limit:
                raise exceptions.OverQuota(overs=sorted(resources_over_limit))

            return quota_api.create_reservation(context, project_id, deltas)
Example #3
0
 def _create_reservation(self,
                         resource_deltas,
                         project_id=None,
                         expiration=None):
     project_id = project_id or self.project_id
     return quota_api.create_reservation(self.context, project_id,
                                         resource_deltas, expiration)
Example #4
0
 def _create_reservation(self,
                         resource_deltas,
                         tenant_id=None,
                         expiration=None):
     tenant_id = tenant_id or self.tenant_id
     return quota_api.create_reservation(self.context, tenant_id,
                                         resource_deltas, expiration)
Example #5
0
    def make_reservation(self, context, project_id, resources, deltas, plugin):
        resources_over_limit = []
        with db_api.CONTEXT_WRITER.using(context):
            # Filter out unlimited resources.
            limits = self.get_project_quotas(context, resources, project_id)
            unlimited_resources = set([
                resource for (resource, limit) in limits.items() if limit < 0
            ])
            requested_resources = (set(deltas.keys()) - unlimited_resources)

            # Count the number of (1) used and (2) reserved resources for this
            # project_id. If any resource limit is exceeded, raise exception.
            for resource_name in requested_resources:
                used_and_reserved = self.get_resource_usage(
                    context, project_id, resources, resource_name)
                resource_num = deltas[resource_name]
                if limits[resource_name] < (used_and_reserved + resource_num):
                    resources_over_limit.append(resource_name)

            if resources_over_limit:
                raise exceptions.OverQuota(overs=sorted(resources_over_limit))

            return quota_api.create_reservation(context, project_id, deltas)
Example #6
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)
Example #7
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.context_manager.writer.using(context):
            # 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 calculate 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)
Example #8
0
 def test_count_reserved(self):
     res = self._create_resource()
     quota_api.create_reservation(self.context, self.tenant_id,
                                  {res.name: 1})
     self.assertEqual(1, res.count_reserved(self.context, self.tenant_id))
Example #9
0
 def _create_reservation(self, resource_deltas,
                         tenant_id=None, expiration=None):
     tenant_id = tenant_id or self.tenant_id
     return quota_api.create_reservation(
         self.context, tenant_id, resource_deltas, expiration)