Esempio n. 1
0
def update_alloc_to_next_hard_limit(context, resources, deltas, res,
                                    expire, project_id):
    from cinder import quota
    QUOTAS = quota.QUOTAS
    reservations = []
    projects = get_project_hierarchy(context, project_id,
                                     parents_as_ids=True).parents
    hard_limit_found = False
    # Update allocated values up the chain til we hit a hard limit or run out
    # of parents
    while projects and not hard_limit_found:
        cur_proj_id = list(projects)[0]
        projects = projects[cur_proj_id]
        cur_quota_lim = QUOTAS.get_by_project_or_default(
            context, cur_proj_id, res)
        hard_limit_found = (cur_quota_lim != -1)
        cur_quota = {res: cur_quota_lim}
        cur_delta = {res: deltas[res]}
        try:
            reservations += db.quota_reserve(
                context, resources, cur_quota, cur_delta, expire,
                CONF.until_refresh, CONF.max_age, cur_proj_id,
                is_allocated_reserve=True)
        except exception.OverQuota:
            db.reservation_rollback(context, reservations)
            raise
    return reservations
Esempio n. 2
0
def update_alloc_to_next_hard_limit(context, resources, deltas, res, expire,
                                    project_id):
    from cinder import quota
    QUOTAS = quota.QUOTAS
    reservations = []
    projects = get_project_hierarchy(context, project_id,
                                     parents_as_ids=True).parents
    hard_limit_found = False
    # Update allocated values up the chain til we hit a hard limit or run out
    # of parents
    while projects and not hard_limit_found:
        cur_proj_id = list(projects)[0]
        projects = projects[cur_proj_id]
        cur_quota_lim = QUOTAS.get_by_project_or_default(
            context, cur_proj_id, res)
        hard_limit_found = (cur_quota_lim != -1)
        cur_quota = {res: cur_quota_lim}
        cur_delta = {res: deltas[res]}
        try:
            reservations += db.quota_reserve(context,
                                             resources,
                                             cur_quota,
                                             cur_delta,
                                             expire,
                                             CONF.until_refresh,
                                             CONF.max_age,
                                             cur_proj_id,
                                             is_allocated_reserve=True)
        except exception.OverQuota:
            db.reservation_rollback(context, reservations)
            raise
    return reservations
Esempio n. 3
0
 def test_reservation_rollback(self):
     reservations = _quota_reserve(self.ctxt, 'project1')
     expected = {
         'project_id': 'project1',
         'volumes': {
             'reserved': 1,
             'in_use': 0
         },
         'gigabytes': {
             'reserved': 2,
             'in_use': 0
         },
     }
     self.assertEqual(
         expected, db.quota_usage_get_all_by_project(self.ctxt, 'project1'))
     db.reservation_get(self.ctxt, reservations[0])
     db.reservation_rollback(self.ctxt, reservations, 'project1')
     self.assertRaises(exception.ReservationNotFound, db.reservation_get,
                       self.ctxt, reservations[0])
     expected = {
         'project_id': 'project1',
         'volumes': {
             'reserved': 0,
             'in_use': 0
         },
         'gigabytes': {
             'reserved': 0,
             'in_use': 0
         },
     }
     self.assertEqual(
         expected, db.quota_usage_get_all_by_project(self.ctxt, 'project1'))
Esempio n. 4
0
    def rollback(self, context, reservations):
        """Roll back reservations.

        :param context: The request context, for access checks.
        :param reservations: A list of the reservation UUIDs, as
                             returned by the reserve() method.
        """

        db.reservation_rollback(context, reservations)
Esempio n. 5
0
    def rollback(self, context, reservations):
        """Roll back reservations.

        :param context: The request context, for access checks.
        :param reservations: A list of the reservation UUIDs, as
                             returned by the reserve() method.
        """

        db.reservation_rollback(context, reservations)
Esempio n. 6
0
    def rollback(self, context, reservations, project_id=None):
        """Roll back reservations.

        :param context: The request context, for access checks.
        :param reservations: A list of the reservation UUIDs, as
                             returned by the reserve() method.
        :param project_id: Specify the project_id if current context
                           is admin and admin wants to impact on
                           common user's tenant.
        """
        # If project_id is None, then we use the project_id in context
        if project_id is None:
            project_id = context.project_id

        db.reservation_rollback(context, reservations, project_id=project_id)
Esempio n. 7
0
    def rollback(self, context, reservations, project_id=None):
        """Roll back reservations.

        :param context: The request context, for access checks.
        :param reservations: A list of the reservation UUIDs, as
                             returned by the reserve() method.
        :param project_id: Specify the project_id if current context
                           is admin and admin wants to impact on
                           common user's tenant.
        """
        # If project_id is None, then we use the project_id in context
        if project_id is None:
            project_id = context.project_id

        db.reservation_rollback(context, reservations, project_id=project_id)
Esempio n. 8
0
 def test_reservation_rollback(self):
     reservations = _quota_reserve(self.ctxt, "project1")
     expected = {
         "project_id": "project1",
         "volumes": {"reserved": 1, "in_use": 0},
         "gigabytes": {"reserved": 2, "in_use": 0},
     }
     self.assertEqual(expected, db.quota_usage_get_all_by_project(self.ctxt, "project1"))
     db.reservation_rollback(self.ctxt, reservations, "project1")
     expected = {
         "project_id": "project1",
         "volumes": {"reserved": 0, "in_use": 0},
         "gigabytes": {"reserved": 0, "in_use": 0},
     }
     self.assertEqual(expected, db.quota_usage_get_all_by_project(self.ctxt, "project1"))
Esempio n. 9
0
 def test_reservation_rollback(self):
     reservations = _quota_reserve(self.ctxt, 'project1')
     expected = {'project_id': 'project1',
                 'volumes': {'reserved': 1, 'in_use': 0},
                 'gigabytes': {'reserved': 2, 'in_use': 0},
                 }
     self.assertEqual(expected,
                      db.quota_usage_get_all_by_project(
                          self.ctxt,
                          'project1'))
     db.reservation_get(self.ctxt, reservations[0])
     db.reservation_rollback(self.ctxt, reservations, 'project1')
     self.assertRaises(exception.ReservationNotFound,
                       db.reservation_get,
                       self.ctxt,
                       reservations[0])
     expected = {'project_id': 'project1',
                 'volumes': {'reserved': 0, 'in_use': 0},
                 'gigabytes': {'reserved': 0, 'in_use': 0},
                 }
     self.assertEqual(expected,
                      db.quota_usage_get_all_by_project(
                          self.ctxt,
                          'project1'))
Esempio n. 10
0
    def update(self, req, id, body):
        """Update Quota for a particular tenant

        This works for hierarchical and non-hierarchical projects. For
        hierarchical projects only immediate parent admin or the
        CLOUD admin are able to perform an update.

        :param req: request
        :param id: target project id that needs to be updated
        :param body: key, value pair that that will be
                     applied to the resources if the update
                     succeeds
        """
        context = req.environ['cinder.context']
        authorize_update(context)
        self.validate_string_length(id,
                                    'quota_set_name',
                                    min_length=1,
                                    max_length=255)

        self.assert_valid_body(body, 'quota_set')

        # Get the optional argument 'skip_validation' from body,
        # if skip_validation is False, then validate existing resource.
        skip_flag = body.get('skip_validation', True)
        if not utils.is_valid_boolstr(skip_flag):
            msg = _("Invalid value '%s' for skip_validation.") % skip_flag
            raise exception.InvalidParameterValue(err=msg)
        skip_flag = strutils.bool_from_string(skip_flag)

        target_project_id = id
        bad_keys = []

        # NOTE(ankit): Pass #1 - In this loop for body['quota_set'].items(),
        # we figure out if we have any bad keys.
        for key, value in body['quota_set'].items():
            if (key not in QUOTAS and key not in NON_QUOTA_KEYS):
                bad_keys.append(key)
                continue

        if len(bad_keys) > 0:
            msg = _("Bad key(s) in quota set: %s") % ",".join(bad_keys)
            raise webob.exc.HTTPBadRequest(explanation=msg)

        # Saving off this value since we need to use it multiple times
        use_nested_quotas = QUOTAS.using_nested_quotas()
        if use_nested_quotas:
            # Get the parent_id of the target project to verify whether we are
            # dealing with hierarchical namespace or non-hierarchical namespace
            target_project = quota_utils.get_project_hierarchy(
                context, target_project_id, parents_as_ids=True)
            parent_id = target_project.parent_id

            if parent_id:
                # Get the children of the project which the token is scoped to
                # in order to know if the target_project is in its hierarchy.
                context_project = quota_utils.get_project_hierarchy(
                    context,
                    context.project_id,
                    subtree_as_ids=True,
                    is_admin_project=context.is_admin)
                self._authorize_update_or_delete(context_project,
                                                 target_project.id, parent_id)

        # NOTE(ankit): Pass #2 - In this loop for body['quota_set'].keys(),
        # we validate the quota limits to ensure that we can bail out if
        # any of the items in the set is bad. Meanwhile we validate value
        # to ensure that the value can't be lower than number of existing
        # resources.
        quota_values = QUOTAS.get_project_quotas(context,
                                                 target_project_id,
                                                 defaults=False)
        valid_quotas = {}
        reservations = []
        for key in body['quota_set'].keys():
            if key in NON_QUOTA_KEYS:
                continue

            value = utils.validate_integer(body['quota_set'][key],
                                           key,
                                           min_value=-1,
                                           max_value=db.MAX_INT)

            # Can't skip the validation of nested quotas since it could mess up
            # hierarchy if parent limit is less than childrens' current usage
            if not skip_flag or use_nested_quotas:
                self._validate_existing_resource(key, value, quota_values)

            if use_nested_quotas:
                try:
                    reservations += self._update_nested_quota_allocated(
                        context, target_project, quota_values, key, value)
                except exception.OverQuota as e:
                    if reservations:
                        db.reservation_rollback(context, reservations)
                    raise webob.exc.HTTPBadRequest(explanation=e.message)

            valid_quotas[key] = value

        # NOTE(ankit): Pass #3 - At this point we know that all the keys and
        # values are valid and we can iterate and update them all in one shot
        # without having to worry about rolling back etc as we have done
        # the validation up front in the 2 loops above.
        for key, value in valid_quotas.items():
            try:
                db.quota_update(context, target_project_id, key, value)
            except exception.ProjectQuotaNotFound:
                db.quota_create(context, target_project_id, key, value)
            except exception.AdminRequired:
                raise webob.exc.HTTPForbidden()

        if reservations:
            db.reservation_commit(context, reservations)
        return {'quota_set': self._get_quotas(context, target_project_id)}
Esempio n. 11
0
    def update(self, req, id, body):
        """Update Quota for a particular tenant

        This works for hierarchical and non-hierarchical projects. For
        hierarchical projects only immediate parent admin or the
        CLOUD admin are able to perform an update.

        :param req: request
        :param id: target project id that needs to be updated
        :param body: key, value pair that that will be
                     applied to the resources if the update
                     succeeds
        """
        context = req.environ['cinder.context']
        authorize_update(context)
        self.validate_string_length(id, 'quota_set_name',
                                    min_length=1, max_length=255)

        self.assert_valid_body(body, 'quota_set')

        # Get the optional argument 'skip_validation' from body,
        # if skip_validation is False, then validate existing resource.
        skip_flag = body.get('skip_validation', True)
        if not utils.is_valid_boolstr(skip_flag):
            msg = _("Invalid value '%s' for skip_validation.") % skip_flag
            raise exception.InvalidParameterValue(err=msg)
        skip_flag = strutils.bool_from_string(skip_flag)

        target_project_id = id
        bad_keys = []

        # NOTE(ankit): Pass #1 - In this loop for body['quota_set'].items(),
        # we figure out if we have any bad keys.
        for key, value in body['quota_set'].items():
            if (key not in QUOTAS and key not in NON_QUOTA_KEYS):
                bad_keys.append(key)
                continue

        if len(bad_keys) > 0:
            msg = _("Bad key(s) in quota set: %s") % ",".join(bad_keys)
            raise webob.exc.HTTPBadRequest(explanation=msg)

        # Saving off this value since we need to use it multiple times
        use_nested_quotas = QUOTAS.using_nested_quotas()
        if use_nested_quotas:
            # Get the parent_id of the target project to verify whether we are
            # dealing with hierarchical namespace or non-hierarchical namespace
            target_project = quota_utils.get_project_hierarchy(
                context, target_project_id, parents_as_ids=True)
            parent_id = target_project.parent_id

            if parent_id:
                # Get the children of the project which the token is scoped to
                # in order to know if the target_project is in its hierarchy.
                context_project = quota_utils.get_project_hierarchy(
                    context, context.project_id, subtree_as_ids=True)
                self._authorize_update_or_delete(context_project,
                                                 target_project.id,
                                                 parent_id)

        # NOTE(ankit): Pass #2 - In this loop for body['quota_set'].keys(),
        # we validate the quota limits to ensure that we can bail out if
        # any of the items in the set is bad. Meanwhile we validate value
        # to ensure that the value can't be lower than number of existing
        # resources.
        quota_values = QUOTAS.get_project_quotas(context, target_project_id,
                                                 defaults=False)
        valid_quotas = {}
        reservations = []
        for key in body['quota_set'].keys():
            if key in NON_QUOTA_KEYS:
                continue

            value = utils.validate_integer(
                body['quota_set'][key], key, min_value=-1,
                max_value=db.MAX_INT)

            # Can't skip the validation of nested quotas since it could mess up
            # hierarchy if parent limit is less than childrens' current usage
            if not skip_flag or use_nested_quotas:
                self._validate_existing_resource(key, value, quota_values)

            if use_nested_quotas:
                try:
                    reservations += self._update_nested_quota_allocated(
                        context, target_project, quota_values, key, value)
                except exception.OverQuota as e:
                    if reservations:
                        db.reservation_rollback(context, reservations)
                    raise webob.exc.HTTPBadRequest(explanation=e.msg)

            valid_quotas[key] = value

        # NOTE(ankit): Pass #3 - At this point we know that all the keys and
        # values are valid and we can iterate and update them all in one shot
        # without having to worry about rolling back etc as we have done
        # the validation up front in the 2 loops above.
        for key, value in valid_quotas.items():
            try:
                db.quota_update(context, target_project_id, key, value)
            except exception.ProjectQuotaNotFound:
                db.quota_create(context, target_project_id, key, value)
            except exception.AdminRequired:
                raise webob.exc.HTTPForbidden()

        if reservations:
            db.reservation_commit(context, reservations)
        return {'quota_set': self._get_quotas(context, target_project_id)}
Esempio n. 12
0
    def update(self, req, id, body):
        """Update Quota for a particular tenant

        This works for hierarchical and non-hierarchical projects. For
        hierarchical projects only immediate parent admin or the
        CLOUD admin are able to perform an update.

        :param req: request
        :param id: target project id that needs to be updated
        :param body: key, value pair that will be applied to
                     the resources if the update succeeds
        """
        context = req.environ['cinder.context']
        target_project_id = id
        context.authorize(policy.UPDATE_POLICY,
                          target={'project_id': target_project_id})
        self.validate_string_length(id, 'quota_set_name',
                                    min_length=1, max_length=255)

        # Saving off this value since we need to use it multiple times
        use_nested_quotas = QUOTAS.using_nested_quotas()
        if use_nested_quotas:
            # Get the parent_id of the target project to verify whether we are
            # dealing with hierarchical namespace or non-hierarchical namespace
            target_project = quota_utils.get_project_hierarchy(
                context, target_project_id, parents_as_ids=True)
            parent_id = target_project.parent_id

            if parent_id:
                # Get the children of the project which the token is scoped to
                # in order to know if the target_project is in its hierarchy.
                context_project = quota_utils.get_project_hierarchy(
                    context, context.project_id, subtree_as_ids=True,
                    is_admin_project=context.is_admin)
                self._authorize_update_or_delete(context_project,
                                                 target_project.id,
                                                 parent_id)

        # NOTE(ankit): Pass #1 - In this loop for body['quota_set'].keys(),
        # we validate the quota limits to ensure that we can bail out if
        # any of the items in the set is bad. Meanwhile we validate value
        # to ensure that the value can't be lower than number of existing
        # resources.
        quota_values = QUOTAS.get_project_quotas(context, target_project_id,
                                                 defaults=False)
        group_quota_values = GROUP_QUOTAS.get_project_quotas(context,
                                                             target_project_id,
                                                             defaults=False)
        quota_values.update(group_quota_values)
        valid_quotas = {}
        reservations = []
        for key in body['quota_set'].keys():
            if key in NON_QUOTA_KEYS:
                continue
            self._validate_existing_resource(key, body['quota_set'][key],
                                             quota_values)

            if use_nested_quotas:
                try:
                    reservations += self._update_nested_quota_allocated(
                        context, target_project, quota_values, key,
                        body['quota_set'][key])
                except exception.OverQuota as e:
                    if reservations:
                        db.reservation_rollback(context, reservations)
                    raise webob.exc.HTTPBadRequest(explanation=e.msg)

            valid_quotas[key] = body['quota_set'][key]

        # NOTE(ankit): Pass #2 - At this point we know that all the keys and
        # values are valid and we can iterate and update them all in one shot
        # without having to worry about rolling back etc as we have done
        # the validation up front in the 2 loops above.
        for key, value in valid_quotas.items():
            try:
                db.quota_update(context, target_project_id, key, value)
            except exception.ProjectQuotaNotFound:
                db.quota_create(context, target_project_id, key, value)
            except exception.AdminRequired:
                raise webob.exc.HTTPForbidden()

        if reservations:
            db.reservation_commit(context, reservations)
        return {'quota_set': self._get_quotas(context, target_project_id)}