Implement Models and Repositories for Resource Quotas
In the interest of smaller CRs, this CR partially implements the quota support blueprint. It includes code model and repository used to store project quota information. Unit and functional tests are also provided to verify the implementation. This CR also cover some small design changes, such as: the new defaults is for unlimited quotas for all resources. Implements: blueprint quota-support-on-barbican-resources Change-Id: Ief79dd36fd35528c9d8586d77e01d1f9ad723f4d
This commit is contained in:
parent
d16c99518c
commit
023ccbbefc
@ -16,20 +16,28 @@ import pecan
|
||||
|
||||
from barbican import api
|
||||
from barbican.api import controllers
|
||||
from barbican.common import exception
|
||||
from barbican.common import quota
|
||||
from barbican.common import resources as res
|
||||
from barbican.common import utils
|
||||
from barbican.common import validators
|
||||
from barbican import i18n as u
|
||||
|
||||
|
||||
LOG = utils.getLogger(__name__)
|
||||
|
||||
|
||||
def _project_quotas_not_found():
|
||||
"""Throw exception indicating project quotas not found."""
|
||||
pecan.abort(404, u._('Not Found. Sorry but your project quotas are in '
|
||||
'another castle.'))
|
||||
|
||||
|
||||
class QuotasController(controllers.ACLMixin):
|
||||
"""Handles quota retrieval requests."""
|
||||
|
||||
def __init__(self, quota_repo=None):
|
||||
def __init__(self):
|
||||
LOG.debug('=== Creating QuotasController ===')
|
||||
self.repo = quota_repo
|
||||
self.quota_driver = quota.QuotaDriver()
|
||||
|
||||
@pecan.expose(generic=True)
|
||||
@ -40,18 +48,18 @@ class QuotasController(controllers.ACLMixin):
|
||||
@controllers.handle_exceptions(u._('Quotas'))
|
||||
@controllers.enforce_rbac('quotas:get')
|
||||
def on_get(self, external_project_id, **kwargs):
|
||||
# TODO(dave) implement
|
||||
resp = {'quotas': self.quota_driver.get_defaults()}
|
||||
LOG.debug('=== QuotasController GET ===')
|
||||
project = res.get_or_create_project(external_project_id)
|
||||
resp = self.quota_driver.get_quotas(project.id)
|
||||
return resp
|
||||
|
||||
|
||||
class ProjectQuotasController(controllers.ACLMixin):
|
||||
"""Handles project quota requests."""
|
||||
|
||||
def __init__(self, project_id, project_quota_repo=None):
|
||||
def __init__(self, project_id):
|
||||
LOG.debug('=== Creating ProjectQuotasController ===')
|
||||
self.passed_project_id = project_id
|
||||
self.repo = project_quota_repo
|
||||
self.validator = validators.ProjectQuotaValidator()
|
||||
self.quota_driver = quota.QuotaDriver()
|
||||
|
||||
@ -63,29 +71,26 @@ class ProjectQuotasController(controllers.ACLMixin):
|
||||
@controllers.handle_exceptions(u._('Project Quotas'))
|
||||
@controllers.enforce_rbac('project_quotas:get')
|
||||
def on_get(self, external_project_id, **kwargs):
|
||||
# TODO(dave) implement
|
||||
LOG.debug('=== ProjectQuotasController GET ===')
|
||||
resp = {'project_quotas': self.quota_driver.get_defaults()}
|
||||
resp = self.quota_driver.get_project_quotas(self.passed_project_id)
|
||||
if resp:
|
||||
return resp
|
||||
else:
|
||||
_project_quotas_not_found()
|
||||
|
||||
return resp
|
||||
|
||||
@index.when(method='POST', template='json')
|
||||
@index.when(method='PUT', template='json')
|
||||
@controllers.handle_exceptions(u._('Project Quotas'))
|
||||
@controllers.enforce_rbac('project_quotas:post')
|
||||
def on_post(self, external_project_id, **kwargs):
|
||||
LOG.debug('=== ProjectQuotasController POST ===')
|
||||
@controllers.enforce_rbac('project_quotas:put')
|
||||
def on_put(self, external_project_id, **kwargs):
|
||||
LOG.debug('=== ProjectQuotasController PUT ===')
|
||||
if not pecan.request.body:
|
||||
raise exception.NoDataToProcess()
|
||||
api.load_body(pecan.request,
|
||||
validator=self.validator)
|
||||
# TODO(dave) implement
|
||||
resp = {'project_quotas': {
|
||||
'secrets': 10,
|
||||
'orders': 20,
|
||||
'containers': 10,
|
||||
'transport_keys': 10,
|
||||
'consumers': -1}
|
||||
}
|
||||
LOG.info(u._LI('Post Project Quotas'))
|
||||
return resp
|
||||
self.quota_driver.set_project_quotas(self.passed_project_id,
|
||||
kwargs['project_quotas'])
|
||||
LOG.info(u._LI('Put Project Quotas'))
|
||||
pecan.response.status = 204
|
||||
|
||||
@index.when(method='DELETE', template='json')
|
||||
@utils.allow_all_content_types
|
||||
@ -93,23 +98,26 @@ class ProjectQuotasController(controllers.ACLMixin):
|
||||
@controllers.enforce_rbac('project_quotas:delete')
|
||||
def on_delete(self, external_project_id, **kwargs):
|
||||
LOG.debug('=== ProjectQuotasController DELETE ===')
|
||||
# TODO(dave) implement
|
||||
LOG.info(u._LI('Delete Project Quotas'))
|
||||
pecan.response.status = 204
|
||||
try:
|
||||
self.quota_driver.delete_project_quotas(self.passed_project_id)
|
||||
except exception.NotFound:
|
||||
LOG.info(u._LI('Delete Project Quotas - Project not found'))
|
||||
_project_quotas_not_found()
|
||||
else:
|
||||
LOG.info(u._LI('Delete Project Quotas'))
|
||||
pecan.response.status = 204
|
||||
|
||||
|
||||
class ProjectsQuotasController(controllers.ACLMixin):
|
||||
"""Handles projects quota retrieval requests."""
|
||||
|
||||
def __init__(self, project_quota_repo=None):
|
||||
def __init__(self):
|
||||
LOG.debug('=== Creating ProjectsQuotaController ===')
|
||||
self.repo = project_quota_repo
|
||||
self.quota_driver = quota.QuotaDriver()
|
||||
|
||||
@pecan.expose()
|
||||
def _lookup(self, project_id, *remainder):
|
||||
return ProjectQuotasController(project_id,
|
||||
project_quota_repo=self.repo), remainder
|
||||
return ProjectQuotasController(project_id), remainder
|
||||
|
||||
@pecan.expose(generic=True)
|
||||
def index(self, **kwargs):
|
||||
@ -119,13 +127,8 @@ class ProjectsQuotasController(controllers.ACLMixin):
|
||||
@controllers.handle_exceptions(u._('Project Quotas'))
|
||||
@controllers.enforce_rbac('project_quotas:get')
|
||||
def on_get(self, external_project_id, **kwargs):
|
||||
|
||||
# TODO(dave) implement
|
||||
project1 = {'project_id': "1234",
|
||||
'project_quotas': self.quota_driver.get_defaults()}
|
||||
project2 = {'project_id': "5678",
|
||||
'project_quotas': self.quota_driver.get_defaults()}
|
||||
project_quotas = {"project_quotas": [project1, project2]}
|
||||
resp = project_quotas
|
||||
|
||||
resp = self.quota_driver.get_project_quotas_list(
|
||||
offset_arg=kwargs.get('offset', 0),
|
||||
limit_arg=kwargs.get('limit', None)
|
||||
)
|
||||
return resp
|
||||
|
@ -17,6 +17,10 @@
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
|
||||
from barbican.common import exception
|
||||
from barbican.common import hrefs
|
||||
from barbican.model import repositories as repo
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
UNLIMITED_VALUE = -1
|
||||
@ -26,23 +30,20 @@ quota_opt_group = cfg.OptGroup(name='quotas',
|
||||
title='Quota Options')
|
||||
|
||||
quota_opts = [
|
||||
cfg.BoolOpt('enabled',
|
||||
default=False,
|
||||
help='When True, quotas are enforced.'),
|
||||
cfg.IntOpt('quota_secrets',
|
||||
default=500,
|
||||
default=-1,
|
||||
help='Number of secrets allowed per project'),
|
||||
cfg.IntOpt('quota_orders',
|
||||
default=100,
|
||||
default=-1,
|
||||
help='Number of orders allowed per project'),
|
||||
cfg.IntOpt('quota_containers',
|
||||
default=-1,
|
||||
help='Number of containers allowed per project'),
|
||||
cfg.IntOpt('quota_transport_keys',
|
||||
default=100,
|
||||
default=-1,
|
||||
help='Number of transport keys allowed per project'),
|
||||
cfg.IntOpt('quota_consumers',
|
||||
default=100,
|
||||
default=-1,
|
||||
help='Number of consumers allowed per project'),
|
||||
]
|
||||
|
||||
@ -54,7 +55,15 @@ CONF.register_opts(quota_opts, group=quota_opt_group)
|
||||
class QuotaDriver(object):
|
||||
"""Driver to enforce quotas and obtain quota information."""
|
||||
|
||||
def get_defaults(self):
|
||||
def __init__(self):
|
||||
self.repo = repo.get_project_quotas_repository()
|
||||
|
||||
def _get_resources(self):
|
||||
"""List of resources that can be constrained by a quota"""
|
||||
return ['secrets', 'orders', 'containers', 'transport_keys',
|
||||
'consumers']
|
||||
|
||||
def _get_defaults(self):
|
||||
"""Return list of default quotas"""
|
||||
quotas = {
|
||||
'secrets': CONF.quotas.quota_secrets,
|
||||
@ -65,7 +74,113 @@ class QuotaDriver(object):
|
||||
}
|
||||
return quotas
|
||||
|
||||
def _extract_project_quotas(self, project_quotas_model):
|
||||
"""Convert project quotas model to Python dict
|
||||
|
||||
:param project_quotas_model: Model containing quota information
|
||||
:return: Python dict containing quota information
|
||||
"""
|
||||
resp_quotas = {}
|
||||
for resource in self._get_resources():
|
||||
resp_quotas[resource] = getattr(project_quotas_model, resource)
|
||||
return resp_quotas
|
||||
|
||||
def _compute_effective_quotas(self, configured_quotas):
|
||||
"""Merge configured and default quota information
|
||||
|
||||
When a quota value is not set, use the default value
|
||||
:param configured_quotas: configured quota values
|
||||
:return: effective quotas
|
||||
"""
|
||||
default_quotas = self._get_defaults()
|
||||
resp_quotas = dict(configured_quotas)
|
||||
for resource, quota in resp_quotas.iteritems():
|
||||
if quota is None:
|
||||
resp_quotas[resource] = default_quotas[resource]
|
||||
return resp_quotas
|
||||
|
||||
def _is_unlimited_value(self, v):
|
||||
"""A helper method to check for unlimited value."""
|
||||
return v is not None and v <= UNLIMITED_VALUE
|
||||
|
||||
return v <= UNLIMITED_VALUE
|
||||
def set_project_quotas(self, project_id, parsed_project_quotas):
|
||||
"""Create a new database entry, or update existing one
|
||||
|
||||
:param project_id: ID of project whose quotas are to be set
|
||||
:param parsed_project_quotas: quota values to save in database
|
||||
:return: None
|
||||
"""
|
||||
session = self.repo.get_session()
|
||||
self.repo.create_or_update_by_project_id(
|
||||
project_id, parsed_project_quotas, session=session)
|
||||
session.commit()
|
||||
|
||||
def get_project_quotas(self, project_id):
|
||||
"""Retrieve configured quota information from database
|
||||
|
||||
:param project_id: ID of project for whose value are wanted
|
||||
:return: the values
|
||||
"""
|
||||
session = self.repo.get_session()
|
||||
try:
|
||||
retrieved_project_quotas =\
|
||||
self.repo.get_by_project_id(project_id, session=session)
|
||||
except exception.NotFound:
|
||||
return None
|
||||
resp_quotas = self._extract_project_quotas(retrieved_project_quotas)
|
||||
resp = {'project_quotas': resp_quotas}
|
||||
return resp
|
||||
|
||||
def get_project_quotas_list(self, offset_arg=None, limit_arg=None):
|
||||
"""Return a dict and list of all configured quota information
|
||||
|
||||
:return: a dict and list of a page of quota config info
|
||||
"""
|
||||
session = self.repo.get_session()
|
||||
retrieved_project_quotas, offset, limit, total =\
|
||||
self.repo.get_by_create_date(session=session,
|
||||
offset_arg=offset_arg,
|
||||
limit_arg=limit_arg,
|
||||
suppress_exception=True)
|
||||
resp_quotas = []
|
||||
for quotas in retrieved_project_quotas:
|
||||
list_item = {'project_id': quotas.project_id,
|
||||
'project_quotas':
|
||||
self._extract_project_quotas(quotas)}
|
||||
resp_quotas.append(list_item)
|
||||
resp = {'project_quotas': resp_quotas}
|
||||
resp_overall = hrefs.add_nav_hrefs(
|
||||
'project_quotas', offset, limit, total, resp)
|
||||
resp_overall.update({'total': total})
|
||||
return resp_overall
|
||||
|
||||
def delete_project_quotas(self, project_id):
|
||||
"""Remove configured quota information from database
|
||||
|
||||
:param project_id: ID of project whose quota config will be deleted
|
||||
:raises NotFound: if project has no configured values
|
||||
:return: None
|
||||
"""
|
||||
session = self.repo.get_session()
|
||||
self.repo.delete_by_project_id(project_id,
|
||||
session=session)
|
||||
|
||||
def get_quotas(self, project_id):
|
||||
"""Get the effective quotas for a project
|
||||
|
||||
Effective quotas are based on both configured and default values
|
||||
:param project_id: ID of project for which to get effective quotas
|
||||
:return: dict of effective quota values
|
||||
"""
|
||||
session = self.repo.get_session()
|
||||
try:
|
||||
retrieved_project_quotas =\
|
||||
self.repo.get_by_project_id(project_id,
|
||||
session=session)
|
||||
except exception.NotFound:
|
||||
resp_quotas = self._get_defaults()
|
||||
else:
|
||||
resp_quotas = self._compute_effective_quotas(
|
||||
self._extract_project_quotas(retrieved_project_quotas))
|
||||
resp = {'quotas': resp_quotas}
|
||||
return resp
|
||||
|
@ -1242,3 +1242,76 @@ class ContainerACLUser(BASE, ModelBase):
|
||||
"""Sub-class hook method: return dict of fields."""
|
||||
return {'acl_id': self.acl_id,
|
||||
'user_id': self.user_id}
|
||||
|
||||
|
||||
class ProjectQuotas(BASE, ModelBase):
|
||||
"""Stores Project Quotas.
|
||||
|
||||
Class to define project specific resource quotas.
|
||||
|
||||
Project quota deletes are not soft-deletes.
|
||||
"""
|
||||
|
||||
__tablename__ = 'project_quotas'
|
||||
|
||||
project_id = sa.Column(
|
||||
sa.String(36),
|
||||
# TODO(dave): enforce project exists
|
||||
# sa.ForeignKey('projects.id', name='project_quotas_fk'),
|
||||
index=True,
|
||||
nullable=False)
|
||||
secrets = sa.Column(sa.Integer, nullable=True)
|
||||
orders = sa.Column(sa.Integer, nullable=True)
|
||||
containers = sa.Column(sa.Integer, nullable=True)
|
||||
transport_keys = sa.Column(sa.Integer, nullable=True)
|
||||
consumers = sa.Column(sa.Integer, nullable=True)
|
||||
|
||||
__table_args__ = (sa.UniqueConstraint('project_id',
|
||||
name='project_quotas_uc'),)
|
||||
|
||||
def __init__(self, project_id=None, parsed_project_quotas=None):
|
||||
"""Creates Project Quotas entity from a project and a dict.
|
||||
|
||||
:param project_id: the id of the project whose quotas are to be stored
|
||||
:param parsed_project_quotas: a dict with the keys matching the
|
||||
resources for which quotas are to be set, and the values containing
|
||||
the quota value to be set for this project and that resource.
|
||||
:return: None
|
||||
"""
|
||||
super(ProjectQuotas, self).__init__()
|
||||
|
||||
msg = u._("Must supply non-None {0} argument for ProjectQuotas entry.")
|
||||
|
||||
if project_id is None:
|
||||
raise exception.MissingArgumentError(msg.format("project_id"))
|
||||
self.project_id = project_id
|
||||
|
||||
if parsed_project_quotas is None:
|
||||
self.secrets = None
|
||||
self.orders = None
|
||||
self.containers = None
|
||||
self.transport_keys = None
|
||||
self.consumers = None
|
||||
else:
|
||||
self.secrets = parsed_project_quotas.get('secrets')
|
||||
self.orders = parsed_project_quotas.get('orders')
|
||||
self.containers = parsed_project_quotas.get('containers')
|
||||
self.transport_keys = parsed_project_quotas.get('transport_keys')
|
||||
self.consumers = parsed_project_quotas.get('consumers')
|
||||
|
||||
def _do_extra_dict_fields(self):
|
||||
"""Sub-class hook method: return dict of fields."""
|
||||
ret = {
|
||||
'project_id': self.project_id,
|
||||
}
|
||||
if self.secrets:
|
||||
ret['secrets'] = self.secrets
|
||||
if self.orders:
|
||||
ret['orders'] = self.orders
|
||||
if self.containers:
|
||||
ret['containers'] = self.containers
|
||||
if self.transport_keys:
|
||||
ret['transport_keys'] = self.transport_keys
|
||||
if self.consumers:
|
||||
ret['consumers'] = self.consumers
|
||||
return ret
|
||||
|
@ -61,6 +61,7 @@ _ORDER_RETRY_TASK_REPOSITORY = None
|
||||
_PREFERRED_CA_REPOSITORY = None
|
||||
_PROJECT_REPOSITORY = None
|
||||
_PROJECT_CA_REPOSITORY = None
|
||||
_PROJECT_QUOTAS_REPOSITORY = None
|
||||
_SECRET_ACL_REPOSITORY = None
|
||||
_SECRET_META_REPOSITORY = None
|
||||
_SECRET_REPOSITORY = None
|
||||
@ -1836,6 +1837,126 @@ class ContainerACLRepo(BaseRepo):
|
||||
entity.delete(session=session)
|
||||
|
||||
|
||||
class ProjectQuotasRepo(BaseRepo):
|
||||
"""Repository for the ProjectQuotas entity."""
|
||||
def _do_entity_name(self):
|
||||
"""Sub-class hook: return entity name, such as for debugging."""
|
||||
return "ProjectQuotas"
|
||||
|
||||
def _do_build_get_query(self, entity_id, external_project_id, session):
|
||||
"""Sub-class hook: build a retrieve query."""
|
||||
return session.query(models.ProjectQuotas).filter_by(id=entity_id)
|
||||
|
||||
def _do_validate(self, values):
|
||||
"""Sub-class hook: validate values."""
|
||||
pass
|
||||
|
||||
def get_by_create_date(self, offset_arg=None, limit_arg=None,
|
||||
suppress_exception=False, session=None):
|
||||
"""Returns a list of ProjectQuotas
|
||||
|
||||
The list is ordered by the date they were created at and paged
|
||||
based on the offset and limit fields.
|
||||
|
||||
:param offset_arg: The entity number where the query result should
|
||||
start.
|
||||
:param limit_arg: The maximum amount of entities in the result set.
|
||||
:param suppress_exception: Whether NoResultFound exceptions should be
|
||||
suppressed.
|
||||
:param session: SQLAlchemy session object.
|
||||
:raises NotFound: if no quota config is found for the project
|
||||
:returns: Tuple consisting of (list_of_entities, offset, limit, total).
|
||||
"""
|
||||
|
||||
offset, limit = clean_paging_values(offset_arg, limit_arg)
|
||||
|
||||
session = self.get_session(session)
|
||||
|
||||
query = session.query(models.ProjectQuotas)
|
||||
query = query.order_by(models.ProjectQuotas.created_at)
|
||||
query = query.filter_by(deleted=False)
|
||||
|
||||
start = offset
|
||||
end = offset + limit
|
||||
LOG.debug('Retrieving from %s to %s', start, end)
|
||||
total = query.count()
|
||||
entities = query.offset(start).limit(limit).all()
|
||||
LOG.debug('Number entities retrieved: %s out of %s',
|
||||
len(entities), total)
|
||||
|
||||
if total <= 0 and not suppress_exception:
|
||||
_raise_no_entities_found(self._do_entity_name())
|
||||
|
||||
return entities, offset, limit, total
|
||||
|
||||
def create_or_update_by_project_id(self, project_id, parsed_project_quotas,
|
||||
session=None):
|
||||
"""Create or update Project Quotas config for a project by project_id.
|
||||
|
||||
:param project_id: ID of project whose quota config will be saved
|
||||
:param parsed_project_quotas: Python dict with quota definition
|
||||
:param session: SQLAlchemy session object.
|
||||
:return: None
|
||||
"""
|
||||
session = self.get_session(session)
|
||||
query = session.query(models.ProjectQuotas)
|
||||
query = query.filter_by(project_id=project_id)
|
||||
try:
|
||||
entity = query.one()
|
||||
except sa_orm.exc.NoResultFound:
|
||||
self.create_from(
|
||||
models.ProjectQuotas(project_id, parsed_project_quotas),
|
||||
session=session)
|
||||
else:
|
||||
self._update_values(entity, parsed_project_quotas)
|
||||
|
||||
def get_by_project_id(self, project_id,
|
||||
suppress_exception=False, session=None):
|
||||
"""Return configured Project Quotas for a project by project_id.
|
||||
|
||||
:param project_id: ID of project whose quota config will be deleted
|
||||
:param suppress_exception: when True, NotFound is not raised
|
||||
:param session: SQLAlchemy session object.
|
||||
:raises NotFound: if no quota config is found for the project
|
||||
:return: None or Python dict of project quotas for project
|
||||
"""
|
||||
|
||||
session = self.get_session(session)
|
||||
query = session.query(models.ProjectQuotas)
|
||||
query = query.filter_by(project_id=project_id)
|
||||
try:
|
||||
entity = query.one()
|
||||
except sa_orm.exc.NoResultFound:
|
||||
if suppress_exception:
|
||||
return None
|
||||
else:
|
||||
_raise_no_entities_found(self._do_entity_name())
|
||||
return entity
|
||||
|
||||
def delete_by_project_id(self, project_id,
|
||||
suppress_exception=False, session=None):
|
||||
"""Remove configured Project Quotas for a project by project_id.
|
||||
|
||||
:param project_id: ID of project whose quota config will be deleted
|
||||
:param suppress_exception: when True, NotFound is not raised
|
||||
:param session: SQLAlchemy session object.
|
||||
:raises NotFound: if no quota config is found for the project
|
||||
:return: None
|
||||
"""
|
||||
|
||||
session = self.get_session(session)
|
||||
query = session.query(models.ProjectQuotas)
|
||||
query = query.filter_by(project_id=project_id)
|
||||
try:
|
||||
entity = query.one()
|
||||
except sa_orm.exc.NoResultFound:
|
||||
if suppress_exception:
|
||||
return
|
||||
else:
|
||||
_raise_no_entities_found(self._do_entity_name())
|
||||
entity.delete(session=session)
|
||||
|
||||
|
||||
def get_ca_repository():
|
||||
"""Returns a singleton Secret repository instance."""
|
||||
global _CA_REPOSITORY
|
||||
@ -1925,6 +2046,13 @@ def get_project_ca_repository():
|
||||
ProjectCertificateAuthorityRepo)
|
||||
|
||||
|
||||
def get_project_quotas_repository():
|
||||
"""Returns a singleton Project Quotas repository instance."""
|
||||
global _PROJECT_QUOTAS_REPOSITORY
|
||||
return _get_repository(_PROJECT_QUOTAS_REPOSITORY,
|
||||
ProjectQuotasRepo)
|
||||
|
||||
|
||||
def get_secret_acl_repository():
|
||||
"""Returns a singleton Secret ACL repository instance."""
|
||||
global _SECRET_ACL_REPOSITORY
|
||||
|
@ -23,46 +23,129 @@ class WhenTestingQuotas(utils.BarbicanAPIBaseTestCase):
|
||||
def test_should_get_quotas(self):
|
||||
params = {}
|
||||
resp = self.app.get('/quotas', params)
|
||||
self.assertIn('quotas', resp.namespace)
|
||||
self.assertEqual(200, resp.status_int)
|
||||
quotas_list = resp.json.get('quotas')
|
||||
self.assertEqual({'consumers': -1, 'containers': -1, 'orders': -1,
|
||||
'secrets': -1, 'transport_keys': -1},
|
||||
quotas_list)
|
||||
|
||||
def test_should_get_specific_project_quotas(self):
|
||||
params = {}
|
||||
self.create_a_project_quotas()
|
||||
resp = self.app.get(
|
||||
'/project-quotas/{0}'.format(self.project_id),
|
||||
'/project-quotas/{0}'.format(self.get_test_project_id()),
|
||||
params)
|
||||
self.assertEqual(200, resp.status_int)
|
||||
self.assertIn('project_quotas', resp.namespace)
|
||||
project_quotas = resp.json.get('project_quotas')
|
||||
self.assertEqual({'consumers': 105, 'containers': 103, 'orders': 102,
|
||||
'secrets': 101, 'transport_keys': 104},
|
||||
project_quotas)
|
||||
|
||||
def test_should_return_not_found_get_specific_project_quotas(self):
|
||||
params = {}
|
||||
resp = self.app.get(
|
||||
'/project-quotas/{0}'.format(self.get_test_project_id()),
|
||||
params, expect_errors=True)
|
||||
self.assertEqual(404, resp.status_int)
|
||||
|
||||
def test_should_get_project_quotas_list(self):
|
||||
self.create_project_quotas()
|
||||
params = {}
|
||||
resp = self.app.get('/project-quotas', params)
|
||||
self.assertEqual(200, resp.status_int)
|
||||
self.assertIn('project_quotas', resp.namespace)
|
||||
project_quotas_list = resp.json.get('project_quotas')
|
||||
self.assertEqual(3, len(project_quotas_list))
|
||||
self.assertIn('total', resp.json)
|
||||
|
||||
def test_should_post_project_quotas(self):
|
||||
request = {'project_quotas': {}}
|
||||
resp = self.app.post_json(
|
||||
'/project-quotas/{0}'.format(self.project_id), request)
|
||||
self.assertEqual(200, resp.status_int)
|
||||
|
||||
def test_should_delete_specific_project_quotas(self):
|
||||
def test_should_get_empty_project_quotas_list(self):
|
||||
params = {}
|
||||
resp = self.app.delete(
|
||||
'/project-quotas/{0}'.format(self.project_id), params)
|
||||
resp = self.app.get('/project-quotas', params)
|
||||
self.assertEqual(200, resp.status_int)
|
||||
project_quotas_list = resp.json.get('project_quotas')
|
||||
self.assertEqual([], project_quotas_list)
|
||||
self.assertIn('total', resp.json)
|
||||
|
||||
def test_pagination_attributes(self):
|
||||
for index in range(11):
|
||||
self.create_a_project_quotas(index)
|
||||
|
||||
params = {'limit': '2', 'offset': '2'}
|
||||
resp = self.app.get('/project-quotas', params)
|
||||
|
||||
self.assertEqual(200, resp.status_int)
|
||||
self.assertIn('previous', resp.json)
|
||||
self.assertIn('next', resp.json)
|
||||
|
||||
previous_ref = resp.json.get('previous')
|
||||
next_ref = resp.json.get('next')
|
||||
|
||||
self.assertIn('offset=0', previous_ref)
|
||||
self.assertIn('offset=4', next_ref)
|
||||
|
||||
def test_should_put_project_quotas(self):
|
||||
request = {'project_quotas': {}}
|
||||
resp = self.app.put_json(
|
||||
'/project-quotas/{0}'.format(self.project_id), request)
|
||||
self.assertEqual(204, resp.status_int)
|
||||
|
||||
def test_check_post_quotas_not_allowed(self):
|
||||
"""POST not allowed operation for /quotas"""
|
||||
params = {}
|
||||
resp = self.app.post('/quotas/', params, expect_errors=True)
|
||||
self.assertEqual(405, resp.status_int)
|
||||
def test_should_return_bad_value_put_project_quotas(self):
|
||||
request = '{"project_quotas": {"secrets": "foo"}}'
|
||||
resp = self.app.put(
|
||||
'/project-quotas/{0}'.format(self.project_id),
|
||||
request,
|
||||
headers={'Content-Type': 'application/json'},
|
||||
expect_errors=True)
|
||||
self.assertEqual(400, resp.status_int)
|
||||
|
||||
def test_check_put_project_quotas_not_allowed(self):
|
||||
def test_should_return_bad_data_put_project_quotas(self):
|
||||
"""PUT not allowed operation for /project-quotas/{project-id}"""
|
||||
params = {'bad data'}
|
||||
resp = self.app.put(
|
||||
'/project-quotas/{0}'.format(self.project_id),
|
||||
params, expect_errors=True)
|
||||
self.assertEqual(400, resp.status_int)
|
||||
|
||||
def test_should_return_no_payload_for_put_project_quotas(self):
|
||||
"""PUT not allowed operation for /project-quotas/{project-id}"""
|
||||
params = {}
|
||||
resp = self.app.put(
|
||||
'/project-quotas/{0}'.format(self.project_id),
|
||||
params, expect_errors=True)
|
||||
self.assertEqual(400, resp.status_int)
|
||||
|
||||
def test_should_delete_specific_project_quotas(self):
|
||||
params = {}
|
||||
self.create_a_project_quotas()
|
||||
resp = self.app.delete(
|
||||
'/project-quotas/{0}'.format(self.get_test_project_id()),
|
||||
params)
|
||||
self.assertEqual(204, resp.status_int)
|
||||
|
||||
def test_should_return_not_found_delete_specific_project_quotas(self):
|
||||
params = {}
|
||||
resp = self.app.delete(
|
||||
'/project-quotas/{0}'.format('dummy'),
|
||||
params, expect_errors=True)
|
||||
self.assertEqual(404, resp.status_int)
|
||||
|
||||
def test_check_put_quotas_not_allowed(self):
|
||||
"""PuT not allowed operation for /quotas"""
|
||||
params = {}
|
||||
resp = self.app.put('/quotas/', params, expect_errors=True)
|
||||
self.assertEqual(405, resp.status_int)
|
||||
|
||||
def test_check_put_project_quotas_list_not_allowed(self):
|
||||
"""PUT not allowed operation for /project-quotas"""
|
||||
params = {}
|
||||
resp = self.app.put('/project-quotas', params, expect_errors=True)
|
||||
self.assertEqual(405, resp.status_int)
|
||||
|
||||
def test_check_post_project_quotas_not_allowed(self):
|
||||
"""POST not allowed operation for /project-quotas/{project-id}"""
|
||||
params = {}
|
||||
resp = self.app.post(
|
||||
'/project-quotas/{0}'.format(self.project_id),
|
||||
params, expect_errors=True)
|
||||
self.assertEqual(405, resp.status_int)
|
||||
|
||||
def test_check_post_project_quotas_list_not_allowed(self):
|
||||
@ -71,6 +154,27 @@ class WhenTestingQuotas(utils.BarbicanAPIBaseTestCase):
|
||||
resp = self.app.post('/project-quotas', params, expect_errors=True)
|
||||
self.assertEqual(405, resp.status_int)
|
||||
|
||||
# ----------------------- Helper Functions ---------------------------
|
||||
def get_test_project_id(self, index=1):
|
||||
return 'project' + str(index)
|
||||
|
||||
def create_a_project_quotas(self, index=1):
|
||||
project_id = self.get_test_project_id(index)
|
||||
parsed_project_quotas = {
|
||||
'secrets': index * 100 + 1,
|
||||
'orders': index * 100 + 2,
|
||||
'containers': index * 100 + 3,
|
||||
'transport_keys': index * 100 + 4,
|
||||
'consumers': index * 100 + 5}
|
||||
request = {'project_quotas': parsed_project_quotas}
|
||||
resp = self.app.put_json(
|
||||
'/project-quotas/{0}'.format(project_id), request)
|
||||
self.assertEqual(204, resp.status_int)
|
||||
|
||||
def create_project_quotas(self):
|
||||
for index in [1, 2, 3]:
|
||||
self.create_a_project_quotas(index)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
@ -15,23 +15,44 @@
|
||||
|
||||
import unittest
|
||||
|
||||
from barbican.common import exception
|
||||
from barbican.common import quota
|
||||
from barbican.tests import utils
|
||||
from barbican.tests import database_utils
|
||||
|
||||
|
||||
class WhenTestingQuotaFunctions(utils.BaseTestCase):
|
||||
class WhenTestingQuotaDriverFunctions(database_utils.RepositoryTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(WhenTestingQuotaFunctions, self).setUp()
|
||||
super(WhenTestingQuotaDriverFunctions, self).setUp()
|
||||
self.quota_driver = quota.QuotaDriver()
|
||||
|
||||
def test_get_defaults(self):
|
||||
quotas = self.quota_driver.get_defaults()
|
||||
self.assertEqual(500, quotas['secrets'])
|
||||
self.assertEqual(100, quotas['orders'])
|
||||
quotas = self.quota_driver._get_defaults()
|
||||
self.assertEqual(-1, quotas['secrets'])
|
||||
self.assertEqual(-1, quotas['orders'])
|
||||
self.assertEqual(-1, quotas['containers'])
|
||||
self.assertEqual(100, quotas['transport_keys'])
|
||||
self.assertEqual(100, quotas['consumers'])
|
||||
self.assertEqual(-1, quotas['transport_keys'])
|
||||
self.assertEqual(-1, quotas['consumers'])
|
||||
|
||||
def test_compute_effective_quotas_using_some_defaults(self):
|
||||
configured_quotas = {'consumers': None, 'containers': 66,
|
||||
'orders': None, 'secrets': 55,
|
||||
'transport_keys': None}
|
||||
quotas = self.quota_driver._compute_effective_quotas(configured_quotas)
|
||||
expected_quotas = {'consumers': -1, 'containers': 66,
|
||||
'orders': -1, 'secrets': 55,
|
||||
'transport_keys': -1}
|
||||
self.assertEqual(expected_quotas, quotas)
|
||||
|
||||
def test_compute_effective_quotas_using_all_defaults(self):
|
||||
configured_quotas = {'consumers': None, 'containers': None,
|
||||
'orders': None, 'secrets': None,
|
||||
'transport_keys': None}
|
||||
quotas = self.quota_driver._compute_effective_quotas(configured_quotas)
|
||||
expected_quotas = {'consumers': -1, 'containers': -1,
|
||||
'orders': -1, 'secrets': -1,
|
||||
'transport_keys': -1}
|
||||
self.assertEqual(expected_quotas, quotas)
|
||||
|
||||
def test_is_unlimited_true(self):
|
||||
self.assertTrue(self.quota_driver._is_unlimited_value(-1))
|
||||
@ -39,6 +60,137 @@ class WhenTestingQuotaFunctions(utils.BaseTestCase):
|
||||
def test_is_unlimited_false(self):
|
||||
self.assertFalse(self.quota_driver._is_unlimited_value(1))
|
||||
|
||||
def test_is_unlimited_none_is_false(self):
|
||||
self.assertFalse(self.quota_driver._is_unlimited_value(None))
|
||||
|
||||
def test_should_get_project_quotas(self):
|
||||
self.create_a_test_project_quotas()
|
||||
project_quotas = self.quota_driver.get_project_quotas(
|
||||
self.get_test_project_id())
|
||||
self.assertEqual({'project_quotas':
|
||||
self.get_test_parsed_project_quotas()},
|
||||
project_quotas)
|
||||
|
||||
def test_should_return_not_found_get_project_quotas(self):
|
||||
project_quotas = self.quota_driver.get_project_quotas('dummy')
|
||||
self.assertIsNone(project_quotas)
|
||||
|
||||
def test_should_get_project_quotas_list(self):
|
||||
self.create_a_test_project_quotas()
|
||||
project_quotas = self.quota_driver.get_project_quotas_list()
|
||||
self.assertEqual({'project_quotas': [{
|
||||
'project_id': u'project1',
|
||||
'project_quotas': {'consumers': 105,
|
||||
'containers': 103,
|
||||
'orders': 102,
|
||||
'secrets': 101,
|
||||
'transport_keys': 104}}], 'total': 1},
|
||||
project_quotas)
|
||||
|
||||
def test_should_get_empty_project_quotas_list(self):
|
||||
project_quotas = self.quota_driver.get_project_quotas_list()
|
||||
self.assertEqual({'total': 0, 'project_quotas': []}, project_quotas)
|
||||
|
||||
def test_should_delete_project_quotas(self):
|
||||
self.create_a_test_project_quotas()
|
||||
self.quota_driver.delete_project_quotas(
|
||||
self.get_test_project_id())
|
||||
|
||||
def test_should_raise_not_found_delete_project_quotas(self):
|
||||
self.assertRaises(
|
||||
exception.NotFound,
|
||||
self.quota_driver.delete_project_quotas,
|
||||
'dummy')
|
||||
|
||||
def test_get_project_quotas_with_partial_definition(self):
|
||||
self.create_a_test_project_quotas('partial')
|
||||
project_quotas = self.quota_driver.get_project_quotas(
|
||||
self.get_test_project_id('partial'))
|
||||
self.assertEqual({'project_quotas':
|
||||
self.get_test_response_project_quotas('partial')},
|
||||
project_quotas)
|
||||
|
||||
def test_get_project_quotas_using_empty_definition(self):
|
||||
self.create_a_test_project_quotas('none')
|
||||
project_quotas = self.quota_driver.get_project_quotas(
|
||||
self.get_test_project_id('none'))
|
||||
self.assertEqual({'project_quotas':
|
||||
self.get_test_response_project_quotas('none')},
|
||||
project_quotas)
|
||||
|
||||
def test_get_quotas_using_some_defaults(self):
|
||||
self.create_a_test_project_quotas('partial')
|
||||
quotas = self.quota_driver.get_quotas(
|
||||
self.get_test_project_id('partial'))
|
||||
expected_quotas = {'quotas': {'consumers': -1, 'containers': 66,
|
||||
'orders': -1, 'secrets': 55,
|
||||
'transport_keys': -1}}
|
||||
self.assertEqual(expected_quotas, quotas)
|
||||
|
||||
def test_get_quotas_using_all_defaults(self):
|
||||
quotas = self.quota_driver.get_quotas('not_configured')
|
||||
expected_quotas = {'quotas': {'consumers': -1, 'containers': -1,
|
||||
'orders': -1, 'secrets': -1,
|
||||
'transport_keys': -1}}
|
||||
self.assertEqual(expected_quotas, quotas)
|
||||
|
||||
# ----------------------- Helper Functions ---------------------------
|
||||
def get_test_project_id(self, index=1):
|
||||
if index == 'partial':
|
||||
return 'project_partial'
|
||||
elif index == 'none':
|
||||
return 'project_none'
|
||||
else:
|
||||
return 'project' + str(index)
|
||||
|
||||
def get_test_parsed_project_quotas(self, index=1):
|
||||
if index == 'partial':
|
||||
parsed_project_quotas = {
|
||||
'secrets': 55,
|
||||
'containers': 66}
|
||||
elif index == 'none':
|
||||
parsed_project_quotas = {}
|
||||
else:
|
||||
parsed_project_quotas = {
|
||||
'secrets': index * 100 + 1,
|
||||
'orders': index * 100 + 2,
|
||||
'containers': index * 100 + 3,
|
||||
'transport_keys': index * 100 + 4,
|
||||
'consumers': index * 100 + 5}
|
||||
return parsed_project_quotas
|
||||
|
||||
def get_test_response_project_quotas(self, index=1):
|
||||
if index == 'partial':
|
||||
response_project_quotas = {
|
||||
'secrets': 55,
|
||||
'orders': None,
|
||||
'containers': 66,
|
||||
'transport_keys': None,
|
||||
'consumers': None}
|
||||
elif index == 'none':
|
||||
response_project_quotas = {
|
||||
'secrets': None,
|
||||
'orders': None,
|
||||
'containers': None,
|
||||
'transport_keys': None,
|
||||
'consumers': None}
|
||||
else:
|
||||
response_project_quotas = {
|
||||
'secrets': index * 100 + 1,
|
||||
'orders': index * 100 + 2,
|
||||
'containers': index * 100 + 3,
|
||||
'transport_keys': index * 100 + 4,
|
||||
'consumers': index * 100 + 5}
|
||||
return response_project_quotas
|
||||
|
||||
def create_a_test_project_quotas(self, index=1):
|
||||
project_id = self.get_test_project_id(index)
|
||||
parsed_project_quotas = self.get_test_parsed_project_quotas(index)
|
||||
self.quota_driver.set_project_quotas(project_id, parsed_project_quotas)
|
||||
|
||||
def create_project_quotas(self):
|
||||
for index in [1, 2, 3]:
|
||||
self.create_a_test_project_quotas(index)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
249
barbican/tests/model/repositories/test_repositories_quotas.py
Normal file
249
barbican/tests/model/repositories/test_repositories_quotas.py
Normal file
@ -0,0 +1,249 @@
|
||||
# Copyright (c) 2015 Cisco Systems
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import unittest
|
||||
|
||||
from barbican.common import exception
|
||||
from barbican.model import models
|
||||
from barbican.model import repositories
|
||||
from barbican.tests import database_utils
|
||||
|
||||
|
||||
class WhenTestingProjectQuotasRepo(database_utils.RepositoryTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(WhenTestingProjectQuotasRepo, self).setUp()
|
||||
self.project_quotas_repo = repositories.ProjectQuotasRepo()
|
||||
|
||||
self.project_id_1 = '11111'
|
||||
self.project_id_2 = '22222'
|
||||
self.project_id_3 = '33333'
|
||||
self.parsed_project_quotas_1 = {
|
||||
'secrets': 101,
|
||||
'orders': 102,
|
||||
'containers': 103,
|
||||
'transport_keys': 104,
|
||||
'consumers': 105}
|
||||
self.parsed_project_quotas_2 = {
|
||||
'secrets': 201,
|
||||
'orders': 202,
|
||||
'containers': 203,
|
||||
'transport_keys': 204,
|
||||
'consumers': 205}
|
||||
self.parsed_project_quotas_3 = {
|
||||
'secrets': 301,
|
||||
'containers': 303,
|
||||
'consumers': 305}
|
||||
|
||||
def test_get_list_of_one_project_quotas(self):
|
||||
session = self.project_quotas_repo.get_session()
|
||||
self.project_quotas_repo.create_or_update_by_project_id(
|
||||
self.project_id_1,
|
||||
self.parsed_project_quotas_1,
|
||||
session)
|
||||
session.commit()
|
||||
retrieved_project_quotas, offset, limit, total =\
|
||||
self.project_quotas_repo.get_by_create_date(session=session)
|
||||
self.assertEqual(0, offset)
|
||||
self.assertEqual(10, limit)
|
||||
self.assertEqual(1, total)
|
||||
self.assertEqual([self.project_id_1],
|
||||
[s.project_id for s in retrieved_project_quotas])
|
||||
self.assertEqual([101],
|
||||
[s.secrets for s in retrieved_project_quotas])
|
||||
self.assertEqual([102],
|
||||
[s.orders for s in retrieved_project_quotas])
|
||||
self.assertEqual([103],
|
||||
[s.containers for s in retrieved_project_quotas])
|
||||
self.assertEqual([104],
|
||||
[s.transport_keys for s in retrieved_project_quotas])
|
||||
self.assertEqual([105],
|
||||
[s.consumers for s in retrieved_project_quotas])
|
||||
|
||||
def test_get_list_of_two_project_quotas(self):
|
||||
session = self.project_quotas_repo.get_session()
|
||||
self.project_quotas_repo.create_or_update_by_project_id(
|
||||
self.project_id_1,
|
||||
self.parsed_project_quotas_1,
|
||||
session)
|
||||
self.project_quotas_repo.create_or_update_by_project_id(
|
||||
self.project_id_2,
|
||||
self.parsed_project_quotas_2,
|
||||
session)
|
||||
session.commit()
|
||||
retrieved_project_quotas, offset, limit, total =\
|
||||
self.project_quotas_repo.get_by_create_date(session=session)
|
||||
self.assertEqual(0, offset)
|
||||
self.assertEqual(10, limit)
|
||||
self.assertEqual(2, total)
|
||||
self.assertItemsEqual([self.project_id_1, self.project_id_2],
|
||||
[s.project_id for s in retrieved_project_quotas])
|
||||
self.assertItemsEqual([101, 201],
|
||||
[s.secrets for s in retrieved_project_quotas])
|
||||
self.assertItemsEqual([102, 202],
|
||||
[s.orders for s in retrieved_project_quotas])
|
||||
self.assertItemsEqual([103, 203],
|
||||
[s.containers for s in retrieved_project_quotas])
|
||||
self.assertItemsEqual([104, 204],
|
||||
[s.transport_keys for s in
|
||||
retrieved_project_quotas])
|
||||
self.assertItemsEqual([105, 205],
|
||||
[s.consumers for s in retrieved_project_quotas])
|
||||
|
||||
def test_should_raise_get_list_of_zero_project_quotas(self):
|
||||
session = self.project_quotas_repo.get_session()
|
||||
self.assertRaises(
|
||||
exception.NotFound,
|
||||
self.project_quotas_repo.get_by_create_date,
|
||||
session=session,
|
||||
suppress_exception=False)
|
||||
|
||||
def test_should_suppress_get_list_of_zero_project_quotas(self):
|
||||
session = self.project_quotas_repo.get_session()
|
||||
retrieved_project_quotas, offset, limit, total =\
|
||||
self.project_quotas_repo.get_by_create_date(
|
||||
session=session, suppress_exception=True)
|
||||
self.assertEqual(0, offset)
|
||||
self.assertEqual(10, limit)
|
||||
self.assertEqual(0, total)
|
||||
|
||||
def test_get_specific_project_quotas(self):
|
||||
session = self.project_quotas_repo.get_session()
|
||||
self.project_quotas_repo.create_or_update_by_project_id(
|
||||
self.project_id_1, self.parsed_project_quotas_1, session)
|
||||
session.commit()
|
||||
retrieved_project_quotas =\
|
||||
self.project_quotas_repo.get_by_project_id(self.project_id_1,
|
||||
session=session)
|
||||
self.assertEqual(self.project_id_1,
|
||||
retrieved_project_quotas.project_id)
|
||||
self.assertEqual(101, retrieved_project_quotas.secrets)
|
||||
self.assertEqual(102, retrieved_project_quotas.orders)
|
||||
self.assertEqual(103, retrieved_project_quotas.containers)
|
||||
self.assertEqual(104, retrieved_project_quotas.transport_keys)
|
||||
self.assertEqual(105, retrieved_project_quotas.consumers)
|
||||
|
||||
def test_project_quotas_with_some_defaults(self):
|
||||
session = self.project_quotas_repo.get_session()
|
||||
self.project_quotas_repo.create_or_update_by_project_id(
|
||||
self.project_id_3, self.parsed_project_quotas_3, session)
|
||||
session.commit()
|
||||
retrieved_project_quotas =\
|
||||
self.project_quotas_repo.get_by_project_id(self.project_id_3,
|
||||
session=session)
|
||||
self.assertEqual(self.project_id_3,
|
||||
retrieved_project_quotas.project_id)
|
||||
self.assertEqual(301, retrieved_project_quotas.secrets)
|
||||
self.assertIsNone(retrieved_project_quotas.orders)
|
||||
self.assertEqual(303, retrieved_project_quotas.containers)
|
||||
self.assertIsNone(retrieved_project_quotas.transport_keys)
|
||||
self.assertEqual(305, retrieved_project_quotas.consumers)
|
||||
|
||||
def test_update_specific_project_quotas(self):
|
||||
session = self.project_quotas_repo.get_session()
|
||||
|
||||
self.project_quotas_repo.create_or_update_by_project_id(
|
||||
self.project_id_1, self.parsed_project_quotas_1, session)
|
||||
session.commit()
|
||||
self.project_quotas_repo.create_or_update_by_project_id(
|
||||
self.project_id_1, self.parsed_project_quotas_2, session)
|
||||
session.commit()
|
||||
retrieved_project_quotas =\
|
||||
self.project_quotas_repo.get_by_project_id(self.project_id_1,
|
||||
session=session)
|
||||
self.assertEqual(self.project_id_1,
|
||||
retrieved_project_quotas.project_id)
|
||||
self.assertEqual(201, retrieved_project_quotas.secrets)
|
||||
self.assertEqual(202, retrieved_project_quotas.orders)
|
||||
self.assertEqual(203, retrieved_project_quotas.containers)
|
||||
self.assertEqual(204, retrieved_project_quotas.transport_keys)
|
||||
self.assertEqual(205, retrieved_project_quotas.consumers)
|
||||
|
||||
def test_should_raise_get_missing_specific_project_quotas(self):
|
||||
session = self.project_quotas_repo.get_session()
|
||||
self.assertRaises(
|
||||
exception.NotFound,
|
||||
self.project_quotas_repo.get_by_project_id,
|
||||
"dummy",
|
||||
suppress_exception=False,
|
||||
session=session)
|
||||
|
||||
def test_should_suppress_get_missing_specific_project_quotas(self):
|
||||
session = self.project_quotas_repo.get_session()
|
||||
retrieved_project_quotas =\
|
||||
self.project_quotas_repo.get_by_project_id(self.project_id_1,
|
||||
suppress_exception=True,
|
||||
session=session)
|
||||
self.assertIsNone(retrieved_project_quotas)
|
||||
|
||||
def test_get_by_create_date_nothing(self):
|
||||
session = self.project_quotas_repo.get_session()
|
||||
retrieved_project_quotas, offset, limit, total =\
|
||||
self.project_quotas_repo.get_by_create_date(
|
||||
session=session, suppress_exception=True)
|
||||
self.assertEqual([], retrieved_project_quotas)
|
||||
self.assertEqual(0, offset)
|
||||
self.assertEqual(10, limit)
|
||||
self.assertEqual(0, total)
|
||||
|
||||
def test_should_raise_add_duplicate_project_id(self):
|
||||
session = self.project_quotas_repo.get_session()
|
||||
self.project_quotas_repo.create_or_update_by_project_id(
|
||||
self.project_id_1, self.parsed_project_quotas_1, session)
|
||||
session.commit()
|
||||
project_quotas = models.ProjectQuotas(
|
||||
self.project_id_1, self.parsed_project_quotas_2)
|
||||
self.assertRaises(
|
||||
exception.Duplicate,
|
||||
self.project_quotas_repo.create_from,
|
||||
project_quotas,
|
||||
session)
|
||||
|
||||
def test_should_delete(self):
|
||||
session = self.project_quotas_repo.get_session()
|
||||
self.project_quotas_repo.create_or_update_by_project_id(
|
||||
self.project_id_1, self.parsed_project_quotas_1, session)
|
||||
session.commit()
|
||||
self.project_quotas_repo.delete_by_project_id(self.project_id_1,
|
||||
session=session)
|
||||
|
||||
def test_should_raise_delete_not_found(self):
|
||||
session = self.project_quotas_repo.get_session()
|
||||
self.assertRaises(
|
||||
exception.NotFound,
|
||||
self.project_quotas_repo.delete_by_project_id,
|
||||
"dummy",
|
||||
session=session)
|
||||
|
||||
def test_should_suppress_delete_not_found(self):
|
||||
session = self.project_quotas_repo.get_session()
|
||||
self.project_quotas_repo.delete_by_project_id('dummy',
|
||||
suppress_exception=True,
|
||||
session=session)
|
||||
|
||||
def test_do_entity_name(self):
|
||||
self.assertEqual("ProjectQuotas",
|
||||
self.project_quotas_repo._do_entity_name())
|
||||
|
||||
def test_should_raise_not_found_get_by_entity_id(self):
|
||||
session = self.project_quotas_repo.get_session()
|
||||
self.assertRaises(
|
||||
exception.NotFound,
|
||||
self.project_quotas_repo.get,
|
||||
"dummy",
|
||||
session=session)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
@ -14,6 +14,7 @@
|
||||
# limitations under the License.
|
||||
|
||||
import datetime
|
||||
import unittest
|
||||
|
||||
from barbican.common import exception
|
||||
from barbican.model import models
|
||||
@ -506,3 +507,86 @@ class WhenCreatingNewContainerACLUser(utils.BaseTestCase):
|
||||
self.assertRaises(exception.MissingArgumentError,
|
||||
models.ContainerACLUser, self.container_acl_id,
|
||||
None)
|
||||
|
||||
|
||||
class WhenCreatingNewProjectQuotas(utils.BaseTestCase):
|
||||
def setUp(self):
|
||||
super(WhenCreatingNewProjectQuotas, self).setUp()
|
||||
|
||||
def test_create_new_project_quotas(self):
|
||||
project_id = '12345'
|
||||
parsed_project_quotas = {
|
||||
'secrets': 101,
|
||||
'orders': 102,
|
||||
'containers': 103,
|
||||
'transport_keys': 104,
|
||||
'consumers': 105}
|
||||
project_quotas = models.ProjectQuotas(project_id,
|
||||
parsed_project_quotas)
|
||||
|
||||
self.assertEqual('12345', project_quotas.project_id)
|
||||
self.assertEqual(101, project_quotas.secrets)
|
||||
self.assertEqual(102, project_quotas.orders)
|
||||
self.assertEqual(103, project_quotas.containers)
|
||||
self.assertEqual(104, project_quotas.transport_keys)
|
||||
self.assertEqual(105, project_quotas.consumers)
|
||||
|
||||
def test_create_new_project_quotas_with_all_default_quotas(self):
|
||||
project_id = '12345'
|
||||
project_quotas = models.ProjectQuotas(project_id,
|
||||
None)
|
||||
|
||||
self.assertEqual('12345', project_quotas.project_id)
|
||||
self.assertEqual(None, project_quotas.secrets)
|
||||
self.assertEqual(None, project_quotas.orders)
|
||||
self.assertEqual(None, project_quotas.containers)
|
||||
self.assertEqual(None, project_quotas.transport_keys)
|
||||
self.assertEqual(None, project_quotas.consumers)
|
||||
|
||||
def test_create_new_project_quotas_with_some_default_quotas(self):
|
||||
project_id = '12345'
|
||||
parsed_project_quotas = {
|
||||
'secrets': 101,
|
||||
'containers': 103,
|
||||
'consumers': 105}
|
||||
project_quotas = models.ProjectQuotas(project_id,
|
||||
parsed_project_quotas)
|
||||
|
||||
self.assertEqual('12345', project_quotas.project_id)
|
||||
self.assertEqual(101, project_quotas.secrets)
|
||||
self.assertEqual(None, project_quotas.orders)
|
||||
self.assertEqual(103, project_quotas.containers)
|
||||
self.assertEqual(None, project_quotas.transport_keys)
|
||||
self.assertEqual(105, project_quotas.consumers)
|
||||
|
||||
def test_should_throw_exception_missing_project_id(self):
|
||||
self.assertRaises(exception.MissingArgumentError,
|
||||
models.ProjectQuotas, None,
|
||||
None)
|
||||
|
||||
def test_project_quotas_check_to_dict_fields(self):
|
||||
project_id = '12345'
|
||||
parsed_project_quotas = {
|
||||
'secrets': 101,
|
||||
'orders': 102,
|
||||
'containers': 103,
|
||||
'transport_keys': 104,
|
||||
'consumers': 105}
|
||||
project_quotas = models.ProjectQuotas(project_id,
|
||||
parsed_project_quotas)
|
||||
self.assertEqual(project_id,
|
||||
project_quotas.to_dict_fields()['project_id'])
|
||||
self.assertEqual(101,
|
||||
project_quotas.to_dict_fields()['secrets'])
|
||||
self.assertEqual(102,
|
||||
project_quotas.to_dict_fields()['orders'])
|
||||
self.assertEqual(103,
|
||||
project_quotas.to_dict_fields()['containers'])
|
||||
self.assertEqual(104,
|
||||
project_quotas.to_dict_fields()['transport_keys'])
|
||||
self.assertEqual(105,
|
||||
project_quotas.to_dict_fields()['consumers'])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
@ -195,28 +195,26 @@ periodic_interval_max_seconds = 10.0
|
||||
# ====================== Quota Options ===============================
|
||||
|
||||
[quotas]
|
||||
enabled = true
|
||||
# True enforces quotas for the number of resources used by each project.
|
||||
# For each resource, the default maximum number that can be used for
|
||||
# a project is set below. This value can be overridden for each
|
||||
# project through the API. A negative value means no limit. A zero
|
||||
# value effectively disables the resource.
|
||||
|
||||
# default number of secrets allowed per project
|
||||
quota_secrets = 500
|
||||
quota_secrets = -1
|
||||
|
||||
# default number of orders allowed per project
|
||||
quota_orders = 100
|
||||
quota_orders = -1
|
||||
|
||||
# default number of containers allowed per project
|
||||
quota_containers = -1
|
||||
# Note, a negative value signifies unlimited
|
||||
|
||||
# default number of transport_keys allowed per project
|
||||
quota_transport_keys = 100
|
||||
quota_transport_keys = -1
|
||||
|
||||
# default number of consumers allowed per project
|
||||
quota_consumers = 100
|
||||
quota_consumers = -1
|
||||
|
||||
|
||||
# ================= Keystone Notification Options - Application ===============
|
||||
|
@ -70,6 +70,6 @@
|
||||
"container_acls:get": "rule:all_but_audit and rule:container_project_match",
|
||||
"quotas:get": "rule:all_users",
|
||||
"project_quotas:get": "rule:service_admin",
|
||||
"project_quotas:post": "rule:service_admin",
|
||||
"project_quotas:put": "rule:service_admin",
|
||||
"project_quotas:delete": "rule:service_admin"
|
||||
}
|
||||
|
@ -28,10 +28,10 @@ class QuotaBehaviors(base_behaviors.BaseBehaviors):
|
||||
:param user_name: The user name used for REST command
|
||||
:return: a request Response object
|
||||
"""
|
||||
resp = self.client.get('quotas',
|
||||
response_model_type=quota_models.QuotaModel,
|
||||
extra_headers=extra_headers,
|
||||
use_auth=use_auth, user_name=user_name)
|
||||
resp = self.client.get(
|
||||
'quotas', response_model_type=quota_models.QuotasResponseModel,
|
||||
extra_headers=extra_headers,
|
||||
use_auth=use_auth, user_name=user_name)
|
||||
return resp
|
||||
|
||||
def get_project_quotas_list(self, limit=10, offset=0, extra_headers=None,
|
||||
@ -44,21 +44,25 @@ class QuotaBehaviors(base_behaviors.BaseBehaviors):
|
||||
:param extra_headers: extra HTTP headers for the REST request
|
||||
:param use_auth: Boolean for whether to send authentication headers
|
||||
:param user_name: The user name used for REST command
|
||||
:return: a request Response object
|
||||
:return: the response, a list of project quotas and the next/prev refs
|
||||
"""
|
||||
params = {'limit': limit, 'offset': offset}
|
||||
resp = self.client.get(
|
||||
'project-quotas',
|
||||
response_model_type=quota_models.ProjectQuotaModel,
|
||||
response_model_type=quota_models.ProjectQuotaListModel,
|
||||
params=params,
|
||||
extra_headers=extra_headers,
|
||||
use_auth=use_auth, user_name=user_name)
|
||||
|
||||
response = self.get_json(resp)
|
||||
project_quotas, next_ref, prev_ref = self.client.get_list_of_models(
|
||||
response, quota_models.ProjectQuotaModel)
|
||||
# handle expected JSON parsing errors for unauthenticated requests
|
||||
if resp.status_code == 401 and not use_auth:
|
||||
return resp, None, None, None
|
||||
|
||||
return resp, project_quotas
|
||||
project_quotas_list = self.get_json(resp)
|
||||
project_quotas, next_ref, prev_ref = self.client.get_list_of_models(
|
||||
project_quotas_list, quota_models.ProjectQuotaListItemModel)
|
||||
|
||||
return resp, project_quotas, next_ref, prev_ref
|
||||
|
||||
def get_project_quotas(self, project_id, extra_headers=None,
|
||||
use_auth=True, user_name=None):
|
||||
@ -71,9 +75,14 @@ class QuotaBehaviors(base_behaviors.BaseBehaviors):
|
||||
"""
|
||||
resp = self.client.get(
|
||||
'project-quotas/' + project_id,
|
||||
response_model_type=quota_models.ProjectQuotaModel,
|
||||
response_model_type=quota_models.ProjectQuotaOneModel,
|
||||
extra_headers=extra_headers,
|
||||
use_auth=use_auth, user_name=user_name)
|
||||
|
||||
# handle expected JSON parsing errors for unauthenticated requests
|
||||
if resp.status_code == 401 and not use_auth:
|
||||
return resp, None, None, None
|
||||
|
||||
return resp
|
||||
|
||||
def set_project_quotas(self, project_id, request_model, extra_headers=None,
|
||||
@ -86,15 +95,18 @@ class QuotaBehaviors(base_behaviors.BaseBehaviors):
|
||||
:param user_name: The user name used for REST command
|
||||
:return: a request Response object
|
||||
"""
|
||||
resp = self.client.post(
|
||||
resp = self.client.put(
|
||||
'project-quotas/' + project_id,
|
||||
request_model=request_model,
|
||||
response_model_type=quota_models.ProjectQuotaModel,
|
||||
extra_headers=extra_headers,
|
||||
use_auth=use_auth, user_name=user_name)
|
||||
|
||||
self.created_entities.append((project_id, user_name))
|
||||
|
||||
return resp
|
||||
|
||||
def delete_project_quotas(self, project_id, extra_headers=None,
|
||||
expected_fail=False,
|
||||
use_auth=True, user_name=None):
|
||||
"""Handles deleting project quotas
|
||||
|
||||
@ -107,4 +119,16 @@ class QuotaBehaviors(base_behaviors.BaseBehaviors):
|
||||
resp = self.client.delete('project-quotas/' + project_id,
|
||||
extra_headers=extra_headers,
|
||||
use_auth=use_auth, user_name=user_name)
|
||||
|
||||
if not expected_fail:
|
||||
for item in self.created_entities:
|
||||
if item[0] == project_id:
|
||||
self.created_entities.remove(item)
|
||||
|
||||
return resp
|
||||
|
||||
def delete_all_created_quotas(self):
|
||||
"""Delete all of the project_quotas that we have created."""
|
||||
entities = list(self.created_entities)
|
||||
for (acl_ref, user_name) in entities:
|
||||
self.delete_project_quotas(acl_ref, user_name=user_name)
|
||||
|
@ -35,64 +35,140 @@ class QuotasTestCase(base.TestCase):
|
||||
def setUp(self):
|
||||
super(QuotasTestCase, self).setUp()
|
||||
self.behaviors = quota_behaviors.QuotaBehaviors(self.client)
|
||||
self.project_id = self.behaviors.get_project_id_from_name('admin')
|
||||
self.project_id = self.behaviors.get_project_id_from_name(
|
||||
CONF.identity.username)
|
||||
|
||||
def tearDown(self):
|
||||
super(QuotasTestCase, self).tearDown()
|
||||
self.behaviors.delete_all_created_quotas()
|
||||
|
||||
def test_get_quotas(self):
|
||||
"""Get quota information"""
|
||||
def test_get_quotas_with_defaults(self):
|
||||
"""Get effective quota information for own project"""
|
||||
|
||||
resp = self.behaviors.get_quotas()
|
||||
|
||||
self.assertEqual(200, resp.status_code)
|
||||
self.assertEqual(500, resp.model.quotas['secrets'])
|
||||
self.assertEqual(100, resp.model.quotas['transport_keys'])
|
||||
self.assertEqual(100, resp.model.quotas['orders'])
|
||||
self.assertEqual(-1, resp.model.quotas['containers'])
|
||||
self.assertEqual(100, resp.model.quotas['consumers'])
|
||||
self.assertEqual(-1, resp.model.quotas.secrets)
|
||||
self.assertEqual(-1, resp.model.quotas.transport_keys)
|
||||
self.assertEqual(-1, resp.model.quotas.orders)
|
||||
self.assertEqual(-1, resp.model.quotas.containers)
|
||||
self.assertEqual(-1, resp.model.quotas.consumers)
|
||||
|
||||
def test_get_project_quota_list(self):
|
||||
"""Get list of all project quotas"""
|
||||
|
||||
resp, project_quotas_list = self.behaviors.get_project_quotas_list(
|
||||
user_name=service_admin)
|
||||
|
||||
self.assertEqual(200, resp.status_code)
|
||||
for project_quotas in project_quotas_list:
|
||||
self.assertEqual(500, project_quotas.project_quotas['secrets'])
|
||||
self.assertEqual(100,
|
||||
project_quotas.project_quotas['transport_keys'])
|
||||
self.assertEqual(100, project_quotas.project_quotas['orders'])
|
||||
self.assertEqual(-1, project_quotas.project_quotas['containers'])
|
||||
self.assertEqual(100, project_quotas.project_quotas['consumers'])
|
||||
|
||||
def test_get_one_project_quotas(self):
|
||||
def test_get_project_quotas_by_project_id(self):
|
||||
"""Get project quota information for specific project"""
|
||||
|
||||
resp = self.behaviors.get_project_quotas(self.project_id,
|
||||
user_name=service_admin)
|
||||
|
||||
self.assertEqual(200, resp.status_code)
|
||||
self.assertEqual(500, resp.model.project_quotas['secrets'])
|
||||
self.assertEqual(100, resp.model.project_quotas['transport_keys'])
|
||||
self.assertEqual(100, resp.model.project_quotas['orders'])
|
||||
self.assertEqual(-1, resp.model.project_quotas['containers'])
|
||||
self.assertEqual(100, resp.model.project_quotas['consumers'])
|
||||
|
||||
def test_set_project_quotas(self):
|
||||
"""Set project quota information"""
|
||||
|
||||
request_model = quota_models.ProjectQuotaRequestModel(
|
||||
**get_set_project_quotas_request())
|
||||
resp = self.behaviors.set_project_quotas(self.project_id,
|
||||
resp = self.behaviors.set_project_quotas('44444',
|
||||
request_model,
|
||||
user_name=service_admin)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
|
||||
resp = self.behaviors.get_project_quotas('44444',
|
||||
user_name=service_admin)
|
||||
|
||||
self.assertEqual(200, resp.status_code)
|
||||
self.assertEqual(50, resp.model.project_quotas.secrets)
|
||||
self.assertIsNone(resp.model.project_quotas.transport_keys)
|
||||
self.assertEqual(10, resp.model.project_quotas.orders)
|
||||
self.assertEqual(20, resp.model.project_quotas.containers)
|
||||
self.assertIsNone(resp.model.project_quotas.consumers)
|
||||
|
||||
def test_get_project_quotas_by_project_id_not_found(self):
|
||||
"""Get project quota information for specific project"""
|
||||
resp = self.behaviors.get_project_quotas('dummy',
|
||||
user_name=service_admin)
|
||||
self.assertEqual(404, resp.status_code)
|
||||
|
||||
def test_delete_project_quotas(self):
|
||||
"""Delete project quota information"""
|
||||
request_model = quota_models.ProjectQuotaRequestModel(
|
||||
**get_set_project_quotas_request())
|
||||
resp = self.behaviors.set_project_quotas('55555',
|
||||
request_model,
|
||||
user_name=service_admin)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
|
||||
resp = self.behaviors.delete_project_quotas(self.project_id,
|
||||
resp = self.behaviors.delete_project_quotas('55555',
|
||||
user_name=service_admin)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
|
||||
def test_delete_project_quotas_not_found(self):
|
||||
"""Get project quota information"""
|
||||
resp = self.behaviors.delete_project_quotas('dummy',
|
||||
user_name=service_admin)
|
||||
self.assertEqual(404, resp.status_code)
|
||||
|
||||
|
||||
class ProjectQuotasPagingTestCase(base.PagingTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(ProjectQuotasPagingTestCase, self).setUp()
|
||||
self.behaviors = quota_behaviors.QuotaBehaviors(self.client)
|
||||
|
||||
def tearDown(self):
|
||||
self.behaviors.delete_all_created_quotas()
|
||||
super(ProjectQuotasPagingTestCase, self).tearDown()
|
||||
|
||||
def create_model(self):
|
||||
request_model = quota_models.ProjectQuotaRequestModel(
|
||||
**get_set_project_quotas_request())
|
||||
return request_model
|
||||
|
||||
def create_resources(self, count=0, model=None):
|
||||
for x in range(0, count):
|
||||
self.behaviors.set_project_quotas(str(x), model,
|
||||
user_name=service_admin)
|
||||
|
||||
def get_resources(self, limit=10, offset=0, filter=None):
|
||||
return self.behaviors.get_project_quotas_list(
|
||||
limit=limit, offset=offset, user_name=service_admin)
|
||||
|
||||
def set_filter_field(self, unique_str, model):
|
||||
"""ProjectQuotas API does not support filter """
|
||||
pass
|
||||
|
||||
def test_get_project_quota_list_none(self):
|
||||
"""Get list of all project quotas, when there are none"""
|
||||
|
||||
resp, project_quotas_list, _, _ =\
|
||||
self.behaviors.get_project_quotas_list(user_name=service_admin)
|
||||
|
||||
self.assertEqual(200, resp.status_code)
|
||||
self.assertEqual([], project_quotas_list)
|
||||
|
||||
def test_get_project_quota_list_one(self):
|
||||
"""Get list of all project quotas, when there is one"""
|
||||
|
||||
request_model = quota_models.ProjectQuotaRequestModel(
|
||||
**get_set_project_quotas_request())
|
||||
resp = self.behaviors.set_project_quotas('11111',
|
||||
request_model,
|
||||
user_name=service_admin)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
|
||||
resp, project_quotas_list, _, _ =\
|
||||
self.behaviors.get_project_quotas_list(user_name=service_admin)
|
||||
|
||||
self.assertEqual(200, resp.status_code)
|
||||
self.assertEqual(1, len(project_quotas_list))
|
||||
|
||||
def test_get_project_quota_list_two(self):
|
||||
"""Get list of all project quotas, when there is one"""
|
||||
|
||||
request_model = quota_models.ProjectQuotaRequestModel(
|
||||
**get_set_project_quotas_request())
|
||||
resp = self.behaviors.set_project_quotas('22222',
|
||||
request_model,
|
||||
user_name=service_admin)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
resp = self.behaviors.set_project_quotas('33333',
|
||||
request_model,
|
||||
user_name=service_admin)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
|
||||
resp, project_quotas_list, _, _ =\
|
||||
self.behaviors.get_project_quotas_list(user_name=service_admin)
|
||||
|
||||
self.assertEqual(200, resp.status_code)
|
||||
self.assertEqual(2, len(project_quotas_list))
|
||||
|
@ -56,7 +56,7 @@ test_data_rbac_get_project_quotas = {
|
||||
|
||||
test_data_rbac_set_project_quotas = {
|
||||
'with_service_admin': {'user': service_admin, 'admin': service_admin,
|
||||
'expected_return': 200},
|
||||
'expected_return': 204},
|
||||
'with_admin_a': {'user': admin_a, 'admin': admin_a,
|
||||
'expected_return': 403},
|
||||
'with_creator_a': {'user': creator_a, 'admin': admin_a,
|
||||
@ -125,7 +125,7 @@ class RBACQuotasTestCase(base.TestCase):
|
||||
:param admin: the admin of the group owning quotas
|
||||
:param expected_return: the expected http return code
|
||||
"""
|
||||
resp, _ = self.behaviors.get_project_quotas_list(user_name=user)
|
||||
resp, _, _, _ = self.behaviors.get_project_quotas_list(user_name=user)
|
||||
self.assertEqual(expected_return, resp.status_code)
|
||||
|
||||
@utils.parameterized_dataset(test_data_rbac_set_project_quotas)
|
||||
|
@ -17,19 +17,28 @@ limitations under the License.
|
||||
from functionaltests.api.v1.models.base_models import BaseModel
|
||||
|
||||
|
||||
class QuotaModel(BaseModel):
|
||||
class QuotasModel(BaseModel):
|
||||
|
||||
def __init__(self, secrets=None, orders=None, containers=None,
|
||||
transport_keys=None, consumers=None):
|
||||
super(QuotasModel, self).__init__()
|
||||
self.secrets = secrets
|
||||
self.orders = orders
|
||||
self.containers = containers
|
||||
self.transport_keys = transport_keys
|
||||
self.consumers = consumers
|
||||
|
||||
|
||||
class QuotasResponseModel(BaseModel):
|
||||
|
||||
def __init__(self, quotas=None):
|
||||
super(QuotaModel, self).__init__()
|
||||
super(QuotasResponseModel, self).__init__()
|
||||
self.quotas = quotas
|
||||
|
||||
|
||||
class ProjectQuotaModel(BaseModel):
|
||||
|
||||
def __init__(self, project_quotas=None, project_id=None):
|
||||
super(ProjectQuotaModel, self).__init__()
|
||||
self.project_quotas = project_quotas
|
||||
self.project_id = project_id
|
||||
@classmethod
|
||||
def dict_to_obj(cls, input_dict):
|
||||
quotas = QuotasModel(**input_dict.get('quotas'))
|
||||
return cls(quotas=quotas)
|
||||
|
||||
|
||||
class ProjectQuotaRequestModel(BaseModel):
|
||||
@ -37,3 +46,37 @@ class ProjectQuotaRequestModel(BaseModel):
|
||||
def __init__(self, project_quotas=None):
|
||||
super(ProjectQuotaRequestModel, self).__init__()
|
||||
self.project_quotas = project_quotas
|
||||
|
||||
@classmethod
|
||||
def dict_to_obj(cls, input_dict):
|
||||
project_quotas = QuotasModel(**input_dict.get('project_quotas'))
|
||||
return cls(project_quotas=project_quotas)
|
||||
|
||||
|
||||
class ProjectQuotaOneModel(BaseModel):
|
||||
|
||||
def __init__(self, project_quotas=None):
|
||||
super(ProjectQuotaOneModel, self).__init__()
|
||||
self.project_quotas = QuotasModel(**project_quotas)
|
||||
|
||||
|
||||
class ProjectQuotaListItemModel(BaseModel):
|
||||
|
||||
def __init__(self, project_id=None, project_quotas=None):
|
||||
super(ProjectQuotaListItemModel, self).__init__()
|
||||
self.project_id = project_id
|
||||
self.project_quotas = QuotasModel(**project_quotas)
|
||||
|
||||
|
||||
class ProjectQuotaListModel(BaseModel):
|
||||
|
||||
def __init__(self, project_quotas=None):
|
||||
super(ProjectQuotaListModel, self).__init__()
|
||||
self.project_quotas = project_quotas
|
||||
|
||||
@classmethod
|
||||
def dict_to_obj(cls, input_dict):
|
||||
project_quotas = [ProjectQuotaListItemModel(**project_quotas_item)
|
||||
for project_quotas_item in
|
||||
input_dict.get('project_quotas', [])]
|
||||
return cls(project_quotas=project_quotas)
|
||||
|
@ -35,8 +35,9 @@ retval=$?
|
||||
testr slowest
|
||||
|
||||
# run the tests in parallel
|
||||
SKIP=^\(\?\!\.\*ProjectQuotasPagingTestCase\)
|
||||
testr init
|
||||
testr run --parallel --subunit | subunit-trace --no-failure-debug -f
|
||||
testr run $SKIP --parallel --subunit | subunit-trace --no-failure-debug -f
|
||||
retval=$?
|
||||
testr slowest
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user