Esempio n. 1
0
    def _delete_nested_quota(self, ctxt, proj_id):
        # Get the parent_id of the target project to verify whether we are
        # dealing with hierarchical namespace or non-hierarchical
        # namespace.
        try:
            project_quotas = QUOTAS.get_project_quotas(
                ctxt, proj_id, usages=True, defaults=False)
            project_group_quotas = GROUP_QUOTAS.get_project_quotas(
                ctxt, proj_id, usages=True, defaults=False)
            project_quotas.update(project_group_quotas)
        except exception.NotAuthorized:
            raise webob.exc.HTTPForbidden()

        target_project = quota_utils.get_project_hierarchy(
            ctxt, proj_id)
        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(
                ctxt, ctxt.project_id, subtree_as_ids=True)
            self._authorize_update_or_delete(context_project,
                                             target_project.id,
                                             parent_id)

        defaults = QUOTAS.get_defaults(ctxt, proj_id)
        defaults.update(GROUP_QUOTAS.get_defaults(ctxt, proj_id))
        # If the project which is being deleted has allocated part of its
        # quota to its subprojects, then subprojects' quotas should be
        # deleted first.
        for res, value in project_quotas.items():
            if 'allocated' in project_quotas[res].keys():
                if project_quotas[res]['allocated'] > 0:
                    msg = _("About to delete child projects having "
                            "non-zero quota. This should not be performed")
                    raise webob.exc.HTTPBadRequest(explanation=msg)
            # Ensure quota usage wouldn't exceed limit on a delete
            self._validate_existing_resource(
                res, defaults[res], project_quotas)

        try:
            db.quota_destroy_by_project(ctxt, target_project.id)
        except exception.AdminRequired:
            raise webob.exc.HTTPForbidden()

        for res, limit in project_quotas.items():
            # Update child limit to 0 so the parent hierarchy gets it's
            # allocated values updated properly
            self._update_nested_quota_allocated(
                ctxt, target_project, project_quotas, res, 0)
Esempio n. 2
0
    def _delete_nested_quota(self, ctxt, proj_id):
        # Get the parent_id of the target project to verify whether we are
        # dealing with hierarchical namespace or non-hierarchical
        # namespace.
        try:
            project_quotas = QUOTAS.get_project_quotas(
                ctxt, proj_id, usages=True, defaults=False)
        except exception.NotAuthorized:
            raise webob.exc.HTTPForbidden()

        target_project = quota_utils.get_project_hierarchy(
            ctxt, proj_id)
        parent_id = target_project.parent_id
        # If the project which is being deleted has allocated part of its
        # quota to its subprojects, then subprojects' quotas should be
        # deleted first.
        for key, value in project_quotas.items():
            if 'allocated' in project_quotas[key].keys():
                if project_quotas[key]['allocated'] != 0:
                    msg = _("About to delete child projects having "
                            "non-zero quota. This should not be performed")
                    raise webob.exc.HTTPBadRequest(explanation=msg)

        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(
                ctxt, ctxt.project_id, subtree_as_ids=True)
            self._authorize_update_or_delete(context_project,
                                             target_project.id,
                                             parent_id)
            parent_project_quotas = QUOTAS.get_project_quotas(
                ctxt, parent_id)

            # Delete child quota first and later update parent's quota.
            try:
                db.quota_destroy_by_project(ctxt, target_project.id)
            except exception.AdminRequired:
                raise webob.exc.HTTPForbidden()

            # The parent "gives" quota to its child using the "allocated" value
            # and since the child project is getting deleted, we should restore
            # the child projects quota to the parent quota, but lowering it's
            # allocated value
            for key, value in project_quotas.items():
                project_hard_limit = project_quotas[key]['limit']
                parent_allocated = parent_project_quotas[key]['allocated']
                parent_allocated -= project_hard_limit
                db.quota_allocated_update(ctxt, parent_id, key,
                                          parent_allocated)
Esempio n. 3
0
    def _delete_nested_quota(self, ctxt, proj_id):
        # Get the parent_id of the target project to verify whether we are
        # dealing with hierarchical namespace or non-hierarchical
        # namespace.
        try:
            project_quotas = QUOTAS.get_project_quotas(
                ctxt, proj_id, usages=True, defaults=False)
            project_group_quotas = GROUP_QUOTAS.get_project_quotas(
                ctxt, proj_id, usages=True, defaults=False)
            project_quotas.update(project_group_quotas)
        except exception.NotAuthorized:
            raise webob.exc.HTTPForbidden()

        target_project = quota_utils.get_project_hierarchy(
            ctxt, proj_id)
        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(
                ctxt, ctxt.project_id, subtree_as_ids=True)
            self._authorize_update_or_delete(context_project,
                                             target_project.id,
                                             parent_id)

        defaults = QUOTAS.get_defaults(ctxt, proj_id)
        defaults.update(GROUP_QUOTAS.get_defaults(ctxt, proj_id))
        # If the project which is being deleted has allocated part of its
        # quota to its subprojects, then subprojects' quotas should be
        # deleted first.
        for res, value in project_quotas.items():
            if 'allocated' in project_quotas[res].keys():
                if project_quotas[res]['allocated'] > 0:
                    msg = _("About to delete child projects having "
                            "non-zero quota. This should not be performed")
                    raise webob.exc.HTTPBadRequest(explanation=msg)
            # Ensure quota usage wouldn't exceed limit on a delete
            self._validate_existing_resource(
                res, defaults[res], project_quotas)

        db.quota_destroy_by_project(ctxt, target_project.id)

        for res, limit in project_quotas.items():
            # Update child limit to 0 so the parent hierarchy gets it's
            # allocated values updated properly
            self._update_nested_quota_allocated(
                ctxt, target_project, project_quotas, res, 0)
Esempio n. 4
0
 def test_get_project_keystoneclient_v2(self, ksclient_class):
     keystoneclient = ksclient_class.return_value
     keystoneclient.version = 'v2.0'
     expected_project = quota_utils.GenericProjectInfo(
         self.context.project_id, 'v2.0')
     project = quota_utils.get_project_hierarchy(
         self.context, self.context.project_id)
     self.assertEqual(expected_project.__dict__, project.__dict__)
Esempio n. 5
0
 def test_get_project_keystoneclient_v2(self, ksclient_class):
     keystoneclient = ksclient_class.return_value
     keystoneclient.version = 'v2.0'
     expected_project = quota_utils.GenericProjectInfo(
         self.context.project_id, 'v2.0')
     project = quota_utils.get_project_hierarchy(self.context,
                                                 self.context.project_id)
     self.assertEqual(expected_project.__dict__, project.__dict__)
Esempio n. 6
0
 def test__filter_domain_id_from_parents_domain_as_parent(
         self, mock_client):
     # Test with a top level project (domain is direct parent)
     self._setup_mock_ksclient(mock_client, parents={'default': None})
     project = quota_utils.get_project_hierarchy(
         self.context, self.context.project_id, parents_as_ids=True)
     self.assertIsNone(project.parent_id)
     self.assertIsNone(project.parents)
Esempio n. 7
0
 def test__filter_domain_id_from_parents_domain_as_parent(
         self, mock_client):
     # Test with a top level project (domain is direct parent)
     self._setup_mock_ksclient(mock_client, parents={'default': None})
     project = quota_utils.get_project_hierarchy(
         self.context, self.context.project_id, parents_as_ids=True)
     self.assertIsNone(project.parent_id)
     self.assertIsNone(project.parents)
Esempio n. 8
0
 def test__filter_domain_id_from_parents_domain_as_grandparent(
         self, mock_client):
     # Test with a child project (domain is more than a parent)
     self._setup_mock_ksclient(mock_client,
                               parents={'bar': {'default': None}})
     project = quota_utils.get_project_hierarchy(
         self.context, self.context.project_id, parents_as_ids=True)
     self.assertEqual('bar', project.parent_id)
     self.assertEqual({'bar': None}, project.parents)
Esempio n. 9
0
 def test__filter_domain_id_from_parents_no_parents(
         self, mock_client):
     # Test that if top no parents are present (to simulate an older
     # keystone version) things don't blow up
     self._setup_mock_ksclient(mock_client)
     project = quota_utils.get_project_hierarchy(
         self.context, self.context.project_id, parents_as_ids=True)
     self.assertIsNone(project.parent_id)
     self.assertIsNone(project.parents)
Esempio n. 10
0
 def test__filter_domain_id_from_parents_domain_as_grandparent(
         self, mock_client):
     # Test with a child project (domain is more than a parent)
     self._setup_mock_ksclient(mock_client,
                               parents={'bar': {'default': None}})
     project = quota_utils.get_project_hierarchy(
         self.context, self.context.project_id, parents_as_ids=True)
     self.assertEqual('bar', project.parent_id)
     self.assertEqual({'bar': None}, project.parents)
Esempio n. 11
0
 def test__filter_domain_id_from_parents_no_parents(self, mock_client):
     # Test that if top no parents are present (to simulate an older
     # keystone version) things don't blow up
     self._setup_mock_ksclient(mock_client)
     project = quota_utils.get_project_hierarchy(self.context,
                                                 self.context.project_id,
                                                 parents_as_ids=True)
     self.assertIsNone(project.parent_id)
     self.assertIsNone(project.parents)
Esempio n. 12
0
    def show(self, req, id):
        """Show quota for a particular tenant

        This works for hierarchical and non-hierarchical projects. For
        hierarchical projects admin of current project, immediate
        parent of the project or the CLOUD admin are able to perform
        a show.

        :param req: request
        :param id: target project id that needs to be shown
        """
        context = req.environ['cinder.context']
        authorize_show(context)
        params = req.params
        target_project_id = id

        if not hasattr(params, '__call__') and 'usage' in params:
            usage = strutils.bool_from_string(params['usage'])
        else:
            usage = False

        if QUOTAS.using_nested_quotas():
            # With hierarchical projects, only the admin of the current project
            # or the root project has privilege to perform quota show
            # operations.
            target_project = quota_utils.get_project_hierarchy(
                context, target_project_id)
            context_project = quota_utils.get_project_hierarchy(
                context,
                context.project_id,
                subtree_as_ids=True,
                is_admin_project=context.is_admin)

            self._authorize_show(context_project, target_project)

        try:
            sqlalchemy_api.authorize_project_context(context,
                                                     target_project_id)
        except exception.NotAuthorized:
            raise webob.exc.HTTPForbidden()

        quotas = self._get_quotas(context, target_project_id, usage)
        return self._format_quota_set(target_project_id, quotas)
Esempio n. 13
0
 def test__filter_domain_id_from_parents_no_domain_in_parents(
         self, mock_client):
     # Test that if top most parent is not a domain (to simulate an older
     # keystone version) nothing gets removed from the tree
     parents = {'bar': {'foo': None}}
     self._setup_mock_ksclient(mock_client, parents=parents)
     project = quota_utils.get_project_hierarchy(
         self.context, self.context.project_id, parents_as_ids=True)
     self.assertEqual('bar', project.parent_id)
     self.assertEqual(parents, project.parents)
Esempio n. 14
0
 def test__filter_domain_id_from_parents_no_domain_in_parents(
         self, mock_client):
     # Test that if top most parent is not a domain (to simulate an older
     # keystone version) nothing gets removed from the tree
     parents = {'bar': {'foo': None}}
     self._setup_mock_ksclient(mock_client, parents=parents)
     project = quota_utils.get_project_hierarchy(
         self.context, self.context.project_id, parents_as_ids=True)
     self.assertEqual('bar', project.parent_id)
     self.assertEqual(parents, project.parents)
Esempio n. 15
0
 def test_get_project_keystoneclient_v3(self, ksclient_class):
     keystoneclient = ksclient_class.return_value
     keystoneclient.version = 'v3'
     returned_project = self.FakeProject(self.context.project_id, 'bar')
     del returned_project.subtree
     keystoneclient.projects.get.return_value = returned_project
     expected_project = quota_utils.GenericProjectInfo(
         self.context.project_id, 'v3', 'bar')
     project = quota_utils.get_project_hierarchy(self.context,
                                                 self.context.project_id)
     self.assertEqual(expected_project.__dict__, project.__dict__)
Esempio n. 16
0
 def test_get_project_keystoneclient_v3(self, ksclient_class):
     keystoneclient = ksclient_class.return_value
     keystoneclient.version = 'v3'
     returned_project = self.FakeProject(self.context.project_id, 'bar')
     del returned_project.subtree
     keystoneclient.projects.get.return_value = returned_project
     expected_project = quota_utils.GenericProjectInfo(
         self.context.project_id, 'v3', 'bar')
     project = quota_utils.get_project_hierarchy(
         self.context, self.context.project_id)
     self.assertEqual(expected_project.__dict__, project.__dict__)
Esempio n. 17
0
    def show(self, req, id):
        """Show quota for a particular tenant

        This works for hierarchical and non-hierarchical projects. For
        hierarchical projects admin of current project, immediate
        parent of the project or the CLOUD admin are able to perform
        a show.

        :param req: request
        :param id: target project id that needs to be shown
        """
        context = req.environ['cinder.context']
        authorize_show(context)
        params = req.params
        target_project_id = id

        if not hasattr(params, '__call__') and 'usage' in params:
            usage = utils.get_bool_param('usage', params)
        else:
            usage = False

        if QUOTAS.using_nested_quotas():
            # With hierarchical projects, only the admin of the current project
            # or the root project has privilege to perform quota show
            # operations.
            target_project = quota_utils.get_project_hierarchy(
                context, target_project_id)
            context_project = quota_utils.get_project_hierarchy(
                context, context.project_id, subtree_as_ids=True,
                is_admin_project=context.is_admin)

            self._authorize_show(context_project, target_project)

        try:
            sqlalchemy_api.authorize_project_context(context,
                                                     target_project_id)
        except exception.NotAuthorized:
            raise webob.exc.HTTPForbidden()

        quotas = self._get_quotas(context, target_project_id, usage)
        return self._format_quota_set(target_project_id, quotas)
Esempio n. 18
0
    def show(self, req, id):
        """Show quota for a particular tenant

        This works for hierarchical and non-hierarchical projects. For
        hierarchical projects admin of current project, immediate
        parent of the project or the CLOUD admin are able to perform
        a show.

        :param req: request
        :param id: target project id that needs to be shown
        """
        context = req.environ['cinder.context']
        params = req.params
        target_project_id = id
        context.authorize(policy.SHOW_POLICY,
                          target={'project_id': target_project_id})

        if not hasattr(params, '__call__') and 'usage' in params:
            usage = utils.get_bool_param('usage', params)
        else:
            usage = False

        if QUOTAS.using_nested_quotas():
            # With hierarchical projects, only the admin of the current project
            # or the root project has privilege to perform quota show
            # operations.
            target_project = quota_utils.get_project_hierarchy(
                context, target_project_id)
            context_project = quota_utils.get_project_hierarchy(
                context,
                context.project_id,
                subtree_as_ids=True,
                is_admin_project=context.is_admin)

            self._authorize_show(context_project, target_project)

        quotas = self._get_quotas(context, target_project_id, usage)
        return self._format_quota_set(target_project_id, quotas)
Esempio n. 19
0
 def test_get_project_keystoneclient_v3_with_subtree(self, ksclient_class):
     keystoneclient = ksclient_class.return_value
     keystoneclient.version = 'v3'
     returned_project = self.FakeProject(self.context.project_id, 'bar')
     subtree_dict = {'baz': {'quux': None}}
     returned_project.subtree = subtree_dict
     keystoneclient.projects.get.return_value = returned_project
     expected_project = quota_utils.GenericProjectInfo(
         self.context.project_id, 'v3', 'bar', subtree_dict)
     project = quota_utils.get_project_hierarchy(
         self.context, self.context.project_id, subtree_as_ids=True)
     keystoneclient.projects.get.assert_called_once_with(
         self.context.project_id, parents_as_ids=False, subtree_as_ids=True)
     self.assertEqual(expected_project.__dict__, project.__dict__)
Esempio n. 20
0
 def test_get_project_keystoneclient_v3_with_subtree(self, ksclient_class):
     keystoneclient = ksclient_class.return_value
     keystoneclient.version = 'v3'
     returned_project = self.FakeProject(self.context.project_id, 'bar')
     subtree_dict = {'baz': {'quux': None}}
     returned_project.subtree = subtree_dict
     keystoneclient.projects.get.return_value = returned_project
     expected_project = quota_utils.GenericProjectInfo(
         self.context.project_id, 'v3', 'bar', subtree_dict)
     project = quota_utils.get_project_hierarchy(
         self.context, self.context.project_id, subtree_as_ids=True)
     keystoneclient.projects.get.assert_called_once_with(
         self.context.project_id, subtree_as_ids=True)
     self.assertEqual(expected_project.__dict__, project.__dict__)
Esempio n. 21
0
 def _validate_project_and_authorize(self, context, project_id,
                                     policy_check):
     try:
         target_project = quota_utils.get_project_hierarchy(
             context, project_id)
         target_project = {
             'project_id': target_project.id,
             'domain_id': target_project.domain_id
         }
         context.authorize(policy_check, target=target_project)
     except ks_exc.http.NotFound:
         explanation = _("Project with id %s not found." % project_id)
         raise exc.HTTPNotFound(explanation=explanation)
     except exception.NotAuthorized:
         explanation = _("You are not authorized to perform this "
                         "operation.")
         raise exc.HTTPForbidden(explanation=explanation)
Esempio n. 22
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)
            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)
                parent_project_quotas = QUOTAS.get_project_quotas(
                    context, 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 = {}
        allocated_quotas = {}
        for key in body['quota_set'].keys():
            if key in NON_QUOTA_KEYS:
                continue

            if not skip_flag:
                self._validate_existing_resource(key, value, quota_values)

            if use_nested_quotas and parent_id:
                value = self._validate_quota_limit(body['quota_set'], key,
                                                   quota_values,
                                                   parent_project_quotas)

                if value < 0:
                    # TODO(mc_nair): extend to handle -1 limits and recurse up
                    # the hierarchy
                    msg = _("Quota can't be set to -1 for child projects.")
                    raise webob.exc.HTTPBadRequest(explanation=msg)

                original_quota = 0
                if quota_values.get(key):
                    original_quota = quota_values[key]['limit']

                allocated_quotas[key] = (
                    parent_project_quotas[key].get('allocated', 0) + value -
                    original_quota)
            else:
                value = self._validate_quota_limit(body['quota_set'], key)
            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 hierarchical projects, update child's quota first
            # and then parents quota. In future this needs to be an
            # atomic operation.
            if use_nested_quotas and parent_id:
                if key in allocated_quotas.keys():
                    try:
                        db.quota_allocated_update(context, parent_id, key,
                                                  allocated_quotas[key])
                    except exception.ProjectQuotaNotFound:
                        parent_limit = parent_project_quotas[key]['limit']
                        db.quota_create(context, parent_id, key, parent_limit,
                                        allocated=allocated_quotas[key])

        return {'quota_set': self._get_quotas(context, target_project_id)}
Esempio n. 23
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. 24
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)}
Esempio n. 25
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)}