Resource Quota - DB layer changes
Change-Id: Ib1d4d8634eabdba76c5f7f858efc16702ebc0d2a Partially-Implements: blueprint resource-quota
This commit is contained in:
parent
73059c5c20
commit
ccc04d67fc
|
@ -383,6 +383,10 @@ class QuotaAlreadyExists(Conflict):
|
||||||
"for resource %(resource)s.")
|
"for resource %(resource)s.")
|
||||||
|
|
||||||
|
|
||||||
|
class QuotaNotFound(ResourceNotFound):
|
||||||
|
message = _("Quota could not be found: %(msg)s")
|
||||||
|
|
||||||
|
|
||||||
class RegionsListFailed(MagnumException):
|
class RegionsListFailed(MagnumException):
|
||||||
message = _("Failed to list regions.")
|
message = _("Failed to list regions.")
|
||||||
|
|
||||||
|
|
|
@ -358,6 +358,62 @@ class Connection(object):
|
||||||
:returns: A quota record.
|
:returns: A quota record.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def update_quota(self, project_id, values):
|
||||||
|
"""Update quota record.
|
||||||
|
|
||||||
|
:param project_id: The project id.
|
||||||
|
:param values: A dict containing several items used to identify
|
||||||
|
and track quota for a resource in a project.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
{
|
||||||
|
'id': uuidutils.generate_uuid(),
|
||||||
|
'project_id': 'fake_project',
|
||||||
|
'resource': 'fake_resource',
|
||||||
|
'hard_limit': 'fake_hardlimit',
|
||||||
|
}
|
||||||
|
:returns: A quota record.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def delete_quota(self, project_id, resource):
|
||||||
|
"""Delete a quota.
|
||||||
|
|
||||||
|
:param project_id: Project id.
|
||||||
|
:param resource: resource name.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_quota_by_id(self, context, quota_id):
|
||||||
|
"""Return a quota.
|
||||||
|
|
||||||
|
:param context: The security context
|
||||||
|
:param quota_id: The id of a quota.
|
||||||
|
:returns: A quota.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_quota_list(self, context, filters=None, limit=None,
|
||||||
|
marker=None, sort_key=None, sort_dir=None):
|
||||||
|
"""Get quota list.
|
||||||
|
|
||||||
|
Return a list of the specified columns for all quotas that match the
|
||||||
|
specified filters.
|
||||||
|
|
||||||
|
:param context: The security context
|
||||||
|
:param filters: Filters to apply. Defaults to None.
|
||||||
|
|
||||||
|
:param limit: Maximum number of clusters to return.
|
||||||
|
:param marker: the last item of the previous page; we return the next
|
||||||
|
result set.
|
||||||
|
:param sort_key: Attribute by which results should be sorted.
|
||||||
|
:param sort_dir: direction in which results should be sorted.
|
||||||
|
(asc, desc)
|
||||||
|
:returns: A list of tuples of the specified columns.
|
||||||
|
"""
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def quota_get_all_by_project_id(self, project_id):
|
def quota_get_all_by_project_id(self, project_id):
|
||||||
"""Gets Quota record for all the resources in a project.
|
"""Gets Quota record for all the resources in a project.
|
||||||
|
@ -366,3 +422,13 @@ class Connection(object):
|
||||||
|
|
||||||
:returns: Quota record for all resources in a project.
|
:returns: Quota record for all resources in a project.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_quota_by_project_id_resource(self, project_id, resource):
|
||||||
|
"""Gets quota record for the given quota id.
|
||||||
|
|
||||||
|
:param project_id: project id.
|
||||||
|
:param resource: resource name.
|
||||||
|
|
||||||
|
:returns: Quota record.
|
||||||
|
"""
|
||||||
|
|
|
@ -527,8 +527,81 @@ class Connection(api.Connection):
|
||||||
resource=values['resource'])
|
resource=values['resource'])
|
||||||
return quotas
|
return quotas
|
||||||
|
|
||||||
|
def _add_quota_filters(self, query, filters):
|
||||||
|
if filters is None:
|
||||||
|
filters = {}
|
||||||
|
|
||||||
|
possible_filters = ["resource", "project_id"]
|
||||||
|
|
||||||
|
filter_names = set(filters).intersection(possible_filters)
|
||||||
|
filter_dict = {filter_name: filters[filter_name]
|
||||||
|
for filter_name in filter_names}
|
||||||
|
|
||||||
|
query = query.filter_by(**filter_dict)
|
||||||
|
return query
|
||||||
|
|
||||||
|
def get_quota_list(self, context, filters=None, limit=None, marker=None,
|
||||||
|
sort_key=None, sort_dir=None):
|
||||||
|
query = model_query(models.Quota)
|
||||||
|
query = self._add_quota_filters(query, filters)
|
||||||
|
return _paginate_query(models.Quota, limit, marker,
|
||||||
|
sort_key, sort_dir, query)
|
||||||
|
|
||||||
|
def update_quota(self, project_id, values):
|
||||||
|
session = get_session()
|
||||||
|
with session.begin():
|
||||||
|
query = model_query(models.Quota, session=session)
|
||||||
|
resource = values['resource']
|
||||||
|
try:
|
||||||
|
query = query.filter_by(project_id=project_id).filter_by(
|
||||||
|
resource=resource)
|
||||||
|
ref = query.with_lockmode('update').one()
|
||||||
|
except NoResultFound:
|
||||||
|
msg = (_('project_id %(project_id)s resource %(resource)s.') %
|
||||||
|
{'project_id': project_id, 'resource': resource})
|
||||||
|
raise exception.QuotaNotFound(msg=msg)
|
||||||
|
|
||||||
|
ref.update(values)
|
||||||
|
return ref
|
||||||
|
|
||||||
|
def delete_quota(self, project_id, resource):
|
||||||
|
session = get_session()
|
||||||
|
with session.begin():
|
||||||
|
query = model_query(models.Quota, session=session)
|
||||||
|
|
||||||
|
try:
|
||||||
|
query.filter_by(project_id=project_id).filter_by(
|
||||||
|
resource=resource).one()
|
||||||
|
except NoResultFound:
|
||||||
|
msg = (_('project_id %(project_id)s resource %(resource)s.') %
|
||||||
|
{'project_id': project_id, 'resource': resource})
|
||||||
|
raise exception.QuotaNotFound(msg=msg)
|
||||||
|
|
||||||
|
query.delete()
|
||||||
|
|
||||||
|
def get_quota_by_id(self, context, quota_id):
|
||||||
|
query = model_query(models.Quota)
|
||||||
|
query = query.filter_by(id=quota_id)
|
||||||
|
try:
|
||||||
|
return query.one()
|
||||||
|
except NoResultFound:
|
||||||
|
msg = _('quota id %s .') % quota_id
|
||||||
|
raise exception.QuotaNotFound(msg=msg)
|
||||||
|
|
||||||
def quota_get_all_by_project_id(self, project_id):
|
def quota_get_all_by_project_id(self, project_id):
|
||||||
query = model_query(models.Quota)
|
query = model_query(models.Quota)
|
||||||
result = query.filter_by(project_id=project_id).all()
|
result = query.filter_by(project_id=project_id).all()
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def get_quota_by_project_id_resource(self, project_id, resource):
|
||||||
|
query = model_query(models.Quota)
|
||||||
|
query = query.filter_by(project_id=project_id).filter_by(
|
||||||
|
resource=resource)
|
||||||
|
|
||||||
|
try:
|
||||||
|
return query.one()
|
||||||
|
except NoResultFound:
|
||||||
|
msg = (_('project_id %(project_id)s resource %(resource)s.') %
|
||||||
|
{'project_id': project_id, 'resource': resource})
|
||||||
|
raise exception.QuotaNotFound(msg=msg)
|
||||||
|
|
|
@ -39,3 +39,123 @@ class DbQuotaTestCase(base.DbTestCase):
|
||||||
self.assertEqual(q.hard_limit, r.hard_limit)
|
self.assertEqual(q.hard_limit, r.hard_limit)
|
||||||
self.assertEqual(q.project_id, r.project_id)
|
self.assertEqual(q.project_id, r.project_id)
|
||||||
self.assertEqual(q.resource, r.resource)
|
self.assertEqual(q.resource, r.resource)
|
||||||
|
|
||||||
|
def test_get_quota_by_project_id_resource(self):
|
||||||
|
q = utils.create_test_quotas(project_id='123',
|
||||||
|
resource='test-res',
|
||||||
|
hard_limit=5)
|
||||||
|
res = self.dbapi.get_quota_by_project_id_resource('123', 'test-res')
|
||||||
|
self.assertEqual(q.hard_limit, res.hard_limit)
|
||||||
|
self.assertEqual(q.project_id, res.project_id)
|
||||||
|
self.assertEqual(q.resource, res.resource)
|
||||||
|
|
||||||
|
def test_get_quota_by_project_id_resource_not_found(self):
|
||||||
|
utils.create_test_quotas(project_id='123',
|
||||||
|
resource='test-res',
|
||||||
|
hard_limit=5)
|
||||||
|
self.assertRaises(exception.QuotaNotFound,
|
||||||
|
self.dbapi.get_quota_by_project_id_resource,
|
||||||
|
project_id='123',
|
||||||
|
resource='bad-res')
|
||||||
|
|
||||||
|
def test_get_quota_list(self):
|
||||||
|
project_ids = []
|
||||||
|
for i in range(1, 6):
|
||||||
|
project_id = 'proj-'+str(i)
|
||||||
|
utils.create_test_quotas(project_id=project_id)
|
||||||
|
project_ids.append(project_id)
|
||||||
|
res = self.dbapi.get_quota_list(self.context)
|
||||||
|
res_proj_ids = [r.project_id for r in res]
|
||||||
|
self.assertEqual(sorted(project_ids), sorted(res_proj_ids))
|
||||||
|
|
||||||
|
def test_get_quota_list_sorted(self):
|
||||||
|
project_ids = []
|
||||||
|
for i in range(1, 6):
|
||||||
|
project_id = 'proj-'+str(i)
|
||||||
|
utils.create_test_quotas(project_id=project_id)
|
||||||
|
project_ids.append(project_id)
|
||||||
|
res = self.dbapi.get_quota_list(self.context, sort_key='project_id')
|
||||||
|
res_proj_ids = [r.project_id for r in res]
|
||||||
|
self.assertEqual(sorted(project_ids), res_proj_ids)
|
||||||
|
|
||||||
|
def test_get_quota_list_invalid_sort_key(self):
|
||||||
|
project_ids = []
|
||||||
|
for i in range(1, 6):
|
||||||
|
project_id = 'proj-'+str(i)
|
||||||
|
utils.create_test_quotas(project_id=project_id)
|
||||||
|
project_ids.append(project_id)
|
||||||
|
|
||||||
|
self.assertRaises(exception.InvalidParameterValue,
|
||||||
|
self.dbapi.get_quota_list,
|
||||||
|
self.context,
|
||||||
|
sort_key='invalid')
|
||||||
|
|
||||||
|
def test_get_quota_list_with_filters(self):
|
||||||
|
quota1 = utils.create_test_quotas(project_id='proj-1', resource='res1')
|
||||||
|
quota2 = utils.create_test_quotas(project_id='proj-1', resource='res2')
|
||||||
|
quota3 = utils.create_test_quotas(project_id='proj-2', resource='res1')
|
||||||
|
|
||||||
|
res = self.dbapi.get_quota_list(
|
||||||
|
self.context, filters={'resource': 'res2'})
|
||||||
|
self.assertEqual(quota2.project_id, res[0].project_id)
|
||||||
|
|
||||||
|
res = self.dbapi.get_quota_list(
|
||||||
|
self.context, filters={'project_id': 'proj-2'})
|
||||||
|
self.assertEqual(quota3.project_id, res[0].project_id)
|
||||||
|
|
||||||
|
res = self.dbapi.get_quota_list(
|
||||||
|
self.context, filters={'project_id': 'proj-1'})
|
||||||
|
self.assertEqual(sorted([quota1.project_id, quota2.project_id]),
|
||||||
|
sorted([r.project_id for r in res]))
|
||||||
|
|
||||||
|
def test_update_quota(self):
|
||||||
|
q = utils.create_test_quotas(hard_limit=5,
|
||||||
|
project_id='1234',
|
||||||
|
resource='Cluster')
|
||||||
|
|
||||||
|
res = self.dbapi.get_quota_by_project_id_resource('1234', 'Cluster')
|
||||||
|
self.assertEqual(q.hard_limit, res.hard_limit)
|
||||||
|
self.assertEqual(q.project_id, res.project_id)
|
||||||
|
self.assertEqual(q.resource, res.resource)
|
||||||
|
quota_dict = {'resource': 'Cluster', 'hard_limit': 15}
|
||||||
|
self.dbapi.update_quota('1234', quota_dict)
|
||||||
|
res = self.dbapi.get_quota_by_project_id_resource('1234', 'Cluster')
|
||||||
|
self.assertEqual(quota_dict['hard_limit'], res.hard_limit)
|
||||||
|
self.assertEqual(quota_dict['resource'], res.resource)
|
||||||
|
|
||||||
|
def test_update_quota_not_found(self):
|
||||||
|
utils.create_test_quotas(hard_limit=5,
|
||||||
|
project_id='1234',
|
||||||
|
resource='Cluster')
|
||||||
|
quota_dict = {'resource': 'Cluster', 'hard_limit': 15}
|
||||||
|
self.assertRaises(exception.QuotaNotFound,
|
||||||
|
self.dbapi.update_quota,
|
||||||
|
'invalid_proj',
|
||||||
|
quota_dict)
|
||||||
|
|
||||||
|
def test_delete_quota(self):
|
||||||
|
q = utils.create_test_quotas(project_id='123',
|
||||||
|
resource='test-res',
|
||||||
|
hard_limit=5)
|
||||||
|
res = self.dbapi.get_quota_by_project_id_resource('123', 'test-res')
|
||||||
|
self.assertEqual(q.hard_limit, res.hard_limit)
|
||||||
|
self.assertEqual(q.project_id, res.project_id)
|
||||||
|
self.assertEqual(q.resource, res.resource)
|
||||||
|
self.dbapi.delete_quota(q.project_id, q.resource)
|
||||||
|
self.assertRaises(exception.QuotaNotFound,
|
||||||
|
self.dbapi.get_quota_by_project_id_resource,
|
||||||
|
project_id='123',
|
||||||
|
resource='bad-res')
|
||||||
|
|
||||||
|
def test_delete_quota_that_does_not_exist(self):
|
||||||
|
# Make sure that quota does not exist
|
||||||
|
self.assertRaises(exception.QuotaNotFound,
|
||||||
|
self.dbapi.get_quota_by_project_id_resource,
|
||||||
|
project_id='123',
|
||||||
|
resource='bad-res')
|
||||||
|
|
||||||
|
# Now try to delete non-existing quota
|
||||||
|
self.assertRaises(exception.QuotaNotFound,
|
||||||
|
self.dbapi.delete_quota,
|
||||||
|
project_id='123',
|
||||||
|
resource='bad-res')
|
||||||
|
|
|
@ -122,6 +122,32 @@ def create_test_cluster(**kw):
|
||||||
return dbapi.create_cluster(cluster)
|
return dbapi.create_cluster(cluster)
|
||||||
|
|
||||||
|
|
||||||
|
def get_test_quota(**kw):
|
||||||
|
attrs = {
|
||||||
|
'id': kw.get('id', 42),
|
||||||
|
'project_id': kw.get('project_id', 'fake_project'),
|
||||||
|
'resource': kw.get('resource', 'Cluster'),
|
||||||
|
'hard_limit': kw.get('hard_limit', 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
|
def create_test_quota(**kw):
|
||||||
|
"""Create test quota entry in DB and return Quota DB object.
|
||||||
|
|
||||||
|
Function to be used to create test Quota objects in the database.
|
||||||
|
:param kw: kwargs with overriding values for quota's attributes.
|
||||||
|
:returns: Test Quota DB object.
|
||||||
|
"""
|
||||||
|
quota = get_test_quota(**kw)
|
||||||
|
# Let DB generate ID if it isn't specified explicitly
|
||||||
|
if 'id' not in kw:
|
||||||
|
del quota['id']
|
||||||
|
dbapi = db_api.get_instance()
|
||||||
|
return dbapi.create_quota(quota)
|
||||||
|
|
||||||
|
|
||||||
def get_test_x509keypair(**kw):
|
def get_test_x509keypair(**kw):
|
||||||
return {
|
return {
|
||||||
'id': kw.get('id', 42),
|
'id': kw.get('id', 42),
|
||||||
|
@ -187,7 +213,7 @@ def get_test_quotas(**kw):
|
||||||
return {
|
return {
|
||||||
'id': kw.get('', 18),
|
'id': kw.get('', 18),
|
||||||
'project_id': kw.get('project_id', 'fake_project'),
|
'project_id': kw.get('project_id', 'fake_project'),
|
||||||
'resource': kw.get('resource', 'fake_resource'),
|
'resource': kw.get('resource', 'Cluster'),
|
||||||
'hard_limit': kw.get('hard_limit', 10),
|
'hard_limit': kw.get('hard_limit', 10),
|
||||||
'created_at': kw.get('created_at'),
|
'created_at': kw.get('created_at'),
|
||||||
'updated_at': kw.get('updated_at'),
|
'updated_at': kw.get('updated_at'),
|
||||||
|
|
Loading…
Reference in New Issue