Cinder raises an exception when performing an update quota
operation in a subproject in which its parent's quotas weren't
explicitly updated.
This is because cinder will try to find the parent's quotas
in order to update the allocated value. But, since the parent's
quotas are the default quotas (i.e. there are no entries for them
in the database), the operation will raise an error.
Steps to reproduce:
1. Create a project A in keystone;
2. Create a project B in keystone with A as its parent;
3. Try to update the quota value of any resources of project B
(e.g. openstack quota set <id of B> --volumes 2)
This patch fix this bug by adding entries to the database when
performing an update quota operation in a subproject and there
are still no quota associated with the parent project.
Change-Id: Ia732ca9d5a5f59d3973c2656a27b666077c3402a
Closes-bug: #
1495990
free_quota = (parent_project_quotas[key]['limit'] -
parent_project_quotas[key]['in_use'] -
parent_project_quotas[key]['reserved'] -
- parent_project_quotas[key]['allocated'])
+ parent_project_quotas[key].get('allocated', 0))
current = 0
if project_quotas.get(key):
target_project.id,
parent_id)
parent_project_quotas = QUOTAS.get_project_quotas(
- context, parent_id, parent_project_id=parent_id)
+ 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
quota_values,
parent_project_quotas)
allocated_quotas[key] = (
- parent_project_quotas[key]['allocated'] + value)
+ parent_project_quotas[key].get('allocated', 0) + value)
else:
value = self._validate_quota_limit(body['quota_set'], key)
valid_quotas[key] = value
# atomic operation.
if parent_id:
if key in allocated_quotas.keys():
- db.quota_allocated_update(context, parent_id, key,
- allocated_quotas[key])
+ 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,
parent_project_id=parent_id)}
###################
-def quota_create(context, project_id, resource, limit):
+def quota_create(context, project_id, resource, limit, allocated=0):
"""Create a quota for the given project and resource."""
- return IMPL.quota_create(context, project_id, resource, limit)
+ return IMPL.quota_create(context, project_id, resource, limit,
+ allocated=allocated)
def quota_get(context, project_id, resource):
@require_admin_context
-def quota_create(context, project_id, resource, limit):
+def quota_create(context, project_id, resource, limit, allocated):
quota_ref = models.Quota()
quota_ref.project_id = project_id
quota_ref.resource = resource
quota_ref.hard_limit = limit
+ if allocated:
+ quota_ref.allocated = allocated
session = get_session()
with session.begin():
self.assertRaises(webob.exc.HTTPForbidden, self.controller.update,
self.req, self.B.id, body)
+ def test_update_subproject_quota_when_parent_has_default_quotas(self):
+ self.controller._get_project = mock.Mock()
+ self.controller._get_project.side_effect = self._get_project
+ # Since the quotas of the project A were not updated, it will have
+ # default quotas.
+ self.req.environ['cinder.context'].project_id = self.A.id
+ # Update the project B quota.
+ expected = make_body(gigabytes=1000, snapshots=10,
+ volumes=5, backups=5, tenant_id=None)
+ result = self.controller.update(self.req, self.B.id, expected)
+ self.assertDictMatch(expected, result)
+
@mock.patch(
'cinder.api.openstack.wsgi.Controller.validate_string_length')
@mock.patch(