return IMPL.quota_update(context, project_id, resource, limit)
+def quota_update_resource(context, old_res, new_res):
+ """Update resource of quotas."""
+ return IMPL.quota_update_resource(context, old_res, new_res)
+
+
def quota_destroy(context, project_id, resource):
"""Destroy the quota or raise if it does not exist."""
return IMPL.quota_destroy(context, project_id, resource)
return IMPL.quota_class_update(context, class_name, resource, limit)
+def quota_class_update_resource(context, resource, new_resource):
+ """Update resource name in quota_class."""
+ return IMPL.quota_class_update_resource(context, resource, new_resource)
+
+
def quota_class_destroy(context, class_name, resource):
"""Destroy the quota class or raise if it does not exist."""
return IMPL.quota_class_destroy(context, class_name, resource)
return IMPL.reservation_expire(context)
+def quota_usage_update_resource(context, old_res, new_res):
+ """Update resource field in quota_usages."""
+ return IMPL.quota_usage_update_resource(context, old_res, new_res)
+
+
###################
return result
+@require_context
+def _quota_get_by_resource(context, resource, session=None):
+ rows = model_query(context, models.Quota,
+ session=session,
+ read_deleted='no').filter_by(
+ resource=resource).all()
+ return rows
+
+
@require_admin_context
def quota_create(context, project_id, resource, limit, allocated):
quota_ref = models.Quota()
return quota_ref
+@require_context
+def quota_update_resource(context, old_res, new_res):
+ session = get_session()
+ with session.begin():
+ quotas = _quota_get_by_resource(context, old_res, session=session)
+ for quota in quotas:
+ quota.resource = new_res
+
+
@require_admin_context
def quota_allocated_update(context, project_id, resource, allocated):
session = get_session()
return result
+@require_context
+def _quota_class_get_all_by_resource(context, resource, session):
+ result = model_query(context, models.QuotaClass,
+ session=session,
+ read_deleted="no").\
+ filter_by(resource=resource).\
+ all()
+
+ return result
+
+
@require_admin_context
def quota_class_create(context, class_name, resource, limit):
quota_class_ref = models.QuotaClass()
return quota_class_ref
+@require_context
+def quota_class_update_resource(context, old_res, new_res):
+ session = get_session()
+ with session.begin():
+ quota_class_list = _quota_class_get_all_by_resource(
+ context, old_res, session)
+ for quota_class in quota_class_list:
+ quota_class.resource = new_res
+
+
@require_admin_context
def quota_class_destroy(context, class_name, resource):
session = get_session()
return {row.resource: row for row in rows}
+def _get_quota_usages_by_resource(context, session, resource):
+ rows = model_query(context, models.QuotaUsage,
+ deleted="no",
+ session=session).\
+ filter_by(resource=resource).\
+ with_lockmode('update').\
+ all()
+ return rows
+
+
+@require_context
+@_retry_on_deadlock
+def quota_usage_update_resource(context, old_res, new_res):
+ session = get_session()
+ with session.begin():
+ usages = _get_quota_usages_by_resource(context, session, old_res)
+ for usage in usages:
+ usage.resource = new_res
+ usage.until_refresh = 1
+
+
@require_context
@_retry_on_deadlock
def quota_reserve(context, resources, quotas, deltas, expire,
all()
+def _dict_with_usage_id(usages):
+ return {row.id: row for row in usages.values()}
+
+
@require_context
@_retry_on_deadlock
def reservation_commit(context, reservations, project_id=None):
session = get_session()
with session.begin():
usages = _get_quota_usages(context, session, project_id)
+ usages = _dict_with_usage_id(usages)
for reservation in _quota_reservations(session, context, reservations):
- usage = usages[reservation.resource]
+ usage = usages[reservation.usage_id]
if reservation.delta >= 0:
usage.reserved -= reservation.delta
usage.in_use += reservation.delta
session = get_session()
with session.begin():
usages = _get_quota_usages(context, session, project_id)
-
+ usages = _dict_with_usage_id(usages)
for reservation in _quota_reservations(session, context, reservations):
- usage = usages[reservation.resource]
+ usage = usages[reservation.usage_id]
if reservation.delta >= 0:
usage.reserved -= reservation.delta
def register_resources(self, resources):
raise NotImplementedError(_("Cannot register resources"))
+ def update_quota_resource(self, context, old_type_name, new_type_name):
+ """Update resource in quota.
+
+ This is to update resource in quotas, quota_classes, and
+ quota_usages once the name of a volume type is changed.
+
+ :param context: The request context, for access checks.
+ :param old_type_name: old name of volume type.
+ :param new_type_name: new name of volume type.
+ """
+
+ for quota in ('volumes', 'gigabytes', 'snapshots'):
+ old_res = "%s_%s" % (quota, old_type_name)
+ new_res = "%s_%s" % (quota, new_type_name)
+ db.quota_usage_update_resource(context,
+ old_res,
+ new_res)
+ db.quota_class_update_resource(context,
+ old_res,
+ new_res)
+ db.quota_update_resource(context,
+ old_res,
+ new_res)
+
class CGQuotaEngine(QuotaEngine):
"""Represent the consistencygroup quotas."""
import webob
from cinder.api.contrib import types_manage
+from cinder import context
from cinder import exception
from cinder import test
from cinder.tests.unit.api import fakes
self.assertRaises(webob.exc.HTTPBadRequest,
self.controller._update, req, '1', body)
- @mock.patch('cinder.volume.volume_types.update')
@mock.patch('cinder.volume.volume_types.get_volume_type')
- def test_update_only_name(self, mock_get, mock_update):
+ @mock.patch('cinder.db.volume_type_update')
+ @mock.patch('cinder.quota.VolumeTypeQuotaEngine.'
+ 'update_quota_resource')
+ def test_update_only_name(self, mock_update_quota,
+ mock_update, mock_get):
mock_get.return_value = return_volume_types_get_volume_type_updated(
'999')
- body = {"volume_type": {"name": "vol_type_999_999"}}
+ ctxt = context.RequestContext('admin', 'fake', True)
+ body = {"volume_type": {"name": "vol_type_999"}}
req = fakes.HTTPRequest.blank('/v2/fake/types/999')
req.method = 'PUT'
+ req.environ['cinder.context'] = ctxt
self.assertEqual(0, len(self.notifier.notifications))
res_dict = self.controller._update(req, '999', body)
self.assertEqual(1, len(self.notifier.notifications))
+ mock_update_quota.assert_called_once_with(ctxt, 'vol_type_999_999',
+ 'vol_type_999')
self._check_test_results(res_dict,
{'expected_name': 'vol_type_999_999',
'expected_desc': 'vol_type_desc_999'})
updated = db.quota_class_get(self.ctxt, 'test_qc', 'test_resource')
self.assertEqual(43, updated['hard_limit'])
+ def test_quota_class_update_resource(self):
+ old = db.quota_class_get(self.ctxt, 'test_qc', 'test_resource')
+ db.quota_class_update_resource(self.ctxt,
+ 'test_resource',
+ 'test_resource1')
+ new = db.quota_class_get(self.ctxt, 'test_qc', 'test_resource1')
+ self.assertEqual(old.id, new.id)
+ self.assertEqual('test_resource1', new.resource)
+
def test_quota_class_destroy_all_by_name(self):
db.quota_class_create(self.ctxt, 'test2', 'res1', 43)
db.quota_class_create(self.ctxt, 'test2', 'res2', 44)
self.assertEqual('resource1', quota.resource)
self.assertEqual('project1', quota.project_id)
+ def test_quota_update_resource(self):
+ old = db.quota_create(self.ctxt, 'project1', 'resource1', 41)
+ db.quota_update_resource(self.ctxt, 'resource1', 'resource2')
+ new = db.quota_get(self.ctxt, 'project1', 'resource2')
+ self.assertEqual(old.id, new.id)
+ self.assertEqual('resource2', new.resource)
+
def test_quota_update_nonexistent(self):
self.assertRaises(exception.ProjectQuotaNotFound,
db.quota_update,
db.volume_type_destroy(ctx, vtype['id'])
db.volume_type_destroy(ctx, vtype2['id'])
+ def test_update_quota_resource(self):
+ ctx = context.RequestContext('admin', 'admin', is_admin=True)
+
+ engine = quota.VolumeTypeQuotaEngine()
+ engine.update_quota_resource(ctx, 'type1', 'type2')
+
class DbQuotaDriverTestCase(test.TestCase):
def setUp(self):
import datetime
+import mock
import time
from oslo_config import cfg
new_all_vtypes,
'drive type was not deleted')
+ @mock.patch('cinder.quota.VolumeTypeQuotaEngine.'
+ 'update_quota_resource')
+ def test_update_volume_type_name(self, mock_update_quota):
+ type_ref = volume_types.create(self.ctxt,
+ self.vol_type1_name,
+ self.vol_type1_specs,
+ description=self.vol_type1_description)
+ new_type_name = self.vol_type1_name + '_updated'
+ volume_types.update(self.ctxt,
+ type_ref.id,
+ new_type_name,
+ None)
+ mock_update_quota.assert_called_once_with(self.ctxt,
+ self.vol_type1_name,
+ new_type_name)
+ volume_types.destroy(self.ctxt, type_ref.id)
+
def test_create_volume_type_with_invalid_params(self):
"""Ensure exception will be returned."""
vol_type_invalid_specs = "invalid_extra_specs"
from cinder import db
from cinder import exception
from cinder.i18n import _, _LE
-
+from cinder import quota
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
+QUOTAS = quota.QUOTAS
def create(context,
if id is None:
msg = _("id cannot be None")
raise exception.InvalidVolumeType(reason=msg)
+ old_volume_type = get_volume_type(context, id)
try:
type_updated = db.volume_type_update(context,
id,
dict(name=name,
description=description,
is_public=is_public))
+ # Rename resource in quota if volume type name is changed.
+ if name:
+ old_type_name = old_volume_type.get('name')
+ if old_type_name != name:
+ QUOTAS.update_quota_resource(context,
+ old_type_name,
+ name)
except db_exc.DBError:
LOG.exception(_LE('DB error:'))
raise exception.VolumeTypeUpdateFailed(id=id)