Implement Configuration, Controllers, and Validators for Resource Quotas
In the interest of smaller CRs, this CR partially implements the quota support blueprint. It includes code for configuration, controller, and validator. Also, the framework for unit and functional tests. The controllers process the URL rsources /qoutas and /project-quotas. The configuration code reads the quota default values from the [quotas] section of barbican.conf. The validator code checks the validity of the JSON sent with a POST /project-quotas/ API command. Implements: blueprint quota-support-on-barbican-resources Change-Id: Iad09b19cf6b9a6fa6b29d8b99e3f72172f801070
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
# 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 pecan
|
||||
|
||||
from barbican import api
|
||||
from barbican.api import controllers
|
||||
from barbican.common import quota
|
||||
from barbican.common import utils
|
||||
from barbican.common import validators
|
||||
from barbican import i18n as u
|
||||
|
||||
LOG = utils.getLogger(__name__)
|
||||
|
||||
|
||||
class QuotasController(controllers.ACLMixin):
|
||||
"""Handles quota retrieval requests."""
|
||||
|
||||
def __init__(self, quota_repo=None):
|
||||
LOG.debug('=== Creating QuotasController ===')
|
||||
self.repo = quota_repo
|
||||
self.quota_driver = quota.QuotaDriver()
|
||||
|
||||
@pecan.expose(generic=True)
|
||||
def index(self, **kwargs):
|
||||
pecan.abort(405) # HTTP 405 Method Not Allowed as default
|
||||
|
||||
@index.when(method='GET', template='json')
|
||||
@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()}
|
||||
return resp
|
||||
|
||||
|
||||
class ProjectQuotasController(controllers.ACLMixin):
|
||||
"""Handles project quota requests."""
|
||||
|
||||
def __init__(self, project_id, project_quota_repo=None):
|
||||
LOG.debug('=== Creating ProjectQuotasController ===')
|
||||
self.passed_project_id = project_id
|
||||
self.repo = project_quota_repo
|
||||
self.validator = validators.ProjectQuotaValidator()
|
||||
self.quota_driver = quota.QuotaDriver()
|
||||
|
||||
@pecan.expose(generic=True)
|
||||
def index(self, **kwargs):
|
||||
pecan.abort(405) # HTTP 405 Method Not Allowed as default
|
||||
|
||||
@index.when(method='GET', template='json')
|
||||
@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()}
|
||||
|
||||
return resp
|
||||
|
||||
@index.when(method='POST', 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 ===')
|
||||
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
|
||||
|
||||
@index.when(method='DELETE', template='json')
|
||||
@utils.allow_all_content_types
|
||||
@controllers.handle_exceptions(u._('Project Quotas'))
|
||||
@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
|
||||
|
||||
|
||||
class ProjectsQuotasController(controllers.ACLMixin):
|
||||
"""Handles projects quota retrieval requests."""
|
||||
|
||||
def __init__(self, project_quota_repo=None):
|
||||
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
|
||||
|
||||
@pecan.expose(generic=True)
|
||||
def index(self, **kwargs):
|
||||
pecan.abort(405) # HTTP 405 Method Not Allowed as default
|
||||
|
||||
@index.when(method='GET', template='json')
|
||||
@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
|
||||
|
||||
return resp
|
||||
@@ -17,6 +17,7 @@ from barbican.api import controllers
|
||||
from barbican.api.controllers import cas
|
||||
from barbican.api.controllers import containers
|
||||
from barbican.api.controllers import orders
|
||||
from barbican.api.controllers import quotas
|
||||
from barbican.api.controllers import secrets
|
||||
from barbican.api.controllers import transportkeys
|
||||
from barbican.common import utils
|
||||
@@ -91,14 +92,8 @@ class V1Controller(BaseVersionController):
|
||||
self.containers = containers.ContainersController()
|
||||
self.transport_keys = transportkeys.TransportKeysController()
|
||||
self.cas = cas.CertificateAuthoritiesController()
|
||||
|
||||
self.__controllers = [
|
||||
self.secrets,
|
||||
self.orders,
|
||||
self.containers,
|
||||
self.transport_keys,
|
||||
self.cas,
|
||||
]
|
||||
self.quotas = quotas.QuotasController()
|
||||
setattr(self, 'project-quotas', quotas.ProjectsQuotasController())
|
||||
|
||||
@pecan.expose(generic=True)
|
||||
def index(self):
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
# 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.
|
||||
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
UNLIMITED_VALUE = -1
|
||||
|
||||
|
||||
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,
|
||||
help='Number of secrets allowed per project'),
|
||||
cfg.IntOpt('quota_orders',
|
||||
default=100,
|
||||
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,
|
||||
help='Number of transport keys allowed per project'),
|
||||
cfg.IntOpt('quota_consumers',
|
||||
default=100,
|
||||
help='Number of consumers allowed per project'),
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.register_group(quota_opt_group)
|
||||
CONF.register_opts(quota_opts, group=quota_opt_group)
|
||||
|
||||
|
||||
class QuotaDriver(object):
|
||||
"""Driver to enforce quotas and obtain quota information."""
|
||||
|
||||
def get_defaults(self):
|
||||
"""Return list of default quotas"""
|
||||
quotas = {
|
||||
'secrets': CONF.quotas.quota_secrets,
|
||||
'orders': CONF.quotas.quota_orders,
|
||||
'containers': CONF.quotas.quota_containers,
|
||||
'transport_keys': CONF.quotas.quota_transport_keys,
|
||||
'consumers': CONF.quotas.quota_consumers
|
||||
}
|
||||
return quotas
|
||||
|
||||
def _is_unlimited_value(self, v):
|
||||
"""A helper method to check for unlimited value."""
|
||||
|
||||
return v <= UNLIMITED_VALUE
|
||||
@@ -846,3 +846,36 @@ class NewTransportKeyValidator(ValidatorBase):
|
||||
json_data['transport_key'] = transport_key
|
||||
|
||||
return json_data
|
||||
|
||||
|
||||
class ProjectQuotaValidator(ValidatorBase):
|
||||
"""Validate a new project quota."""
|
||||
|
||||
def __init__(self):
|
||||
self.name = 'Project Quota'
|
||||
|
||||
self.schema = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'project_quotas': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'secrets': {'type': 'integer'},
|
||||
'orders': {'type': 'integer'},
|
||||
'containers': {'type': 'integer'},
|
||||
'transport_keys': {'type': 'integer'},
|
||||
'consumers': {'type': 'integer'}
|
||||
},
|
||||
'additionalProperties': False,
|
||||
}
|
||||
},
|
||||
'required': ['project_quotas'],
|
||||
'additionalProperties': False
|
||||
}
|
||||
|
||||
def validate(self, json_data, parent_schema=None):
|
||||
schema_name = self._full_name(parent_schema)
|
||||
|
||||
self._assert_schema_is_valid(json_data, schema_name)
|
||||
|
||||
return json_data
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
# 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.tests import utils
|
||||
|
||||
|
||||
class WhenTestingQuotas(utils.BarbicanAPIBaseTestCase):
|
||||
|
||||
def test_should_get_quotas(self):
|
||||
params = {}
|
||||
resp = self.app.get('/quotas', params)
|
||||
self.assertIn('quotas', resp.namespace)
|
||||
|
||||
def test_should_get_specific_project_quotas(self):
|
||||
params = {}
|
||||
resp = self.app.get(
|
||||
'/project-quotas/{0}'.format(self.project_id),
|
||||
params)
|
||||
self.assertEqual(200, resp.status_int)
|
||||
self.assertIn('project_quotas', resp.namespace)
|
||||
|
||||
def test_should_get_project_quotas_list(self):
|
||||
params = {}
|
||||
resp = self.app.get('/project-quotas', params)
|
||||
self.assertEqual(200, resp.status_int)
|
||||
self.assertIn('project_quotas', resp.namespace)
|
||||
|
||||
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):
|
||||
params = {}
|
||||
resp = self.app.delete(
|
||||
'/project-quotas/{0}'.format(self.project_id), params)
|
||||
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_check_put_project_quotas_not_allowed(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(405, resp.status_int)
|
||||
|
||||
def test_check_post_project_quotas_list_not_allowed(self):
|
||||
"""POST not allowed operation for /project-quotas"""
|
||||
params = {}
|
||||
resp = self.app.post('/project-quotas', params, expect_errors=True)
|
||||
self.assertEqual(405, resp.status_int)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -0,0 +1,44 @@
|
||||
# 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 quota
|
||||
from barbican.tests import utils
|
||||
|
||||
|
||||
class WhenTestingQuotaFunctions(utils.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(WhenTestingQuotaFunctions, 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'])
|
||||
self.assertEqual(-1, quotas['containers'])
|
||||
self.assertEqual(100, quotas['transport_keys'])
|
||||
self.assertEqual(100, quotas['consumers'])
|
||||
|
||||
def test_is_unlimited_true(self):
|
||||
self.assertTrue(self.quota_driver._is_unlimited_value(-1))
|
||||
|
||||
def test_is_unlimited_false(self):
|
||||
self.assertFalse(self.quota_driver._is_unlimited_value(1))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -1343,5 +1343,32 @@ class WhenTestingAclValidator(utils.BaseTestCase):
|
||||
acl_req)
|
||||
|
||||
|
||||
class WhenTestingProjectQuotasValidator(utils.BaseTestCase):
|
||||
def setUp(self):
|
||||
super(WhenTestingProjectQuotasValidator, self).setUp()
|
||||
self.good_project_quotas = {"project_quotas":
|
||||
{"secrets": 50,
|
||||
"orders": 10,
|
||||
"containers": 20}}
|
||||
self.bad_project_quotas = {"bad key": "bad value"}
|
||||
self.validator = validators.ProjectQuotaValidator()
|
||||
|
||||
def test_should_pass_good_data(self):
|
||||
self.validator.validate(self.good_project_quotas)
|
||||
|
||||
def test_should_pass_empty_properties(self):
|
||||
self.validator.validate({"project_quotas": {}})
|
||||
|
||||
def test_should_raise_bad_data(self):
|
||||
self.assertRaises(excep.InvalidObject,
|
||||
self.validator.validate,
|
||||
self.bad_project_quotas)
|
||||
|
||||
def test_should_raise_empty_dict(self):
|
||||
self.assertRaises(excep.InvalidObject,
|
||||
self.validator.validate,
|
||||
{})
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@@ -192,6 +192,33 @@ initial_delay_seconds = 10.0
|
||||
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
|
||||
|
||||
# default number of orders allowed per project
|
||||
quota_orders = 100
|
||||
|
||||
# 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
|
||||
|
||||
# default number of consumers allowed per project
|
||||
quota_consumers = 100
|
||||
|
||||
|
||||
# ================= Keystone Notification Options - Application ===============
|
||||
|
||||
[keystone_notifications]
|
||||
|
||||
@@ -66,5 +66,9 @@
|
||||
"secret_acls:get": "rule:all_but_audit and rule:secret_project_match",
|
||||
"container_acls:put_patch": "rule:container_project_admin or rule:container_project_creator",
|
||||
"container_acls:delete": "rule:container_project_admin or rule:container_project_creator",
|
||||
"container_acls:get": "rule:all_but_audit and rule:container_project_match"
|
||||
"container_acls:get": "rule:all_but_audit and rule:container_project_match",
|
||||
"quotas:get": "rule:all_users",
|
||||
"project_quotas:get": "rule:admin",
|
||||
"project_quotas:post": "rule:admin",
|
||||
"project_quotas:delete": "rule:admin"
|
||||
}
|
||||
|
||||
@@ -53,3 +53,7 @@ class BaseBehaviors(object):
|
||||
def get_user_id_from_name(self, user_name):
|
||||
"""From a configured user name, get the unique user id from keystone"""
|
||||
return self.client.get_user_id_from_name(user_name)
|
||||
|
||||
def get_project_id_from_name(self, user_name):
|
||||
"""From a configured user name, get the project id from keystone"""
|
||||
return self.client.get_project_id_from_name(user_name)
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
"""
|
||||
Copyright 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.
|
||||
"""
|
||||
from functionaltests.api.v1.behaviors import base_behaviors
|
||||
from functionaltests.api.v1.models import quota_models
|
||||
|
||||
|
||||
class QuotaBehaviors(base_behaviors.BaseBehaviors):
|
||||
|
||||
def get_quotas(self, extra_headers=None,
|
||||
use_auth=True, user_name=None):
|
||||
"""Handles getting quotas
|
||||
|
||||
: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
|
||||
"""
|
||||
resp = self.client.get('quotas',
|
||||
response_model_type=quota_models.QuotaModel,
|
||||
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,
|
||||
use_auth=True, user_name=None):
|
||||
"""Handles getting project quotas
|
||||
|
||||
:param limit: limits number of returned orders (default 10)
|
||||
:param offset: represents how many records to skip before retrieving
|
||||
the list (default 0)
|
||||
: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
|
||||
"""
|
||||
params = {'limit': limit, 'offset': offset}
|
||||
resp = self.client.get(
|
||||
'project-quotas',
|
||||
response_model_type=quota_models.ProjectQuotaModel,
|
||||
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)
|
||||
|
||||
return resp, project_quotas
|
||||
|
||||
def get_project_quotas(self, project_id, extra_headers=None,
|
||||
use_auth=True, user_name=None):
|
||||
"""Handles getting project quotas
|
||||
|
||||
: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
|
||||
"""
|
||||
resp = self.client.get(
|
||||
'project-quotas/' + project_id,
|
||||
response_model_type=quota_models.ProjectQuotaModel,
|
||||
extra_headers=extra_headers,
|
||||
use_auth=use_auth, user_name=user_name)
|
||||
return resp
|
||||
|
||||
def set_project_quotas(self, project_id, request_model, extra_headers=None,
|
||||
use_auth=True, user_name=None):
|
||||
"""Handles setting project quotas
|
||||
|
||||
:param project_id: id of project whose quotas are to be set
|
||||
: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
|
||||
"""
|
||||
resp = self.client.post(
|
||||
'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)
|
||||
return resp
|
||||
|
||||
def delete_project_quotas(self, project_id, extra_headers=None,
|
||||
use_auth=True, user_name=None):
|
||||
"""Handles deleting project quotas
|
||||
|
||||
:param project_id: id of project whose quotas are to be deleted
|
||||
: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
|
||||
"""
|
||||
resp = self.client.delete('project-quotas/' + project_id,
|
||||
extra_headers=extra_headers,
|
||||
use_auth=use_auth, user_name=user_name)
|
||||
return resp
|
||||
@@ -0,0 +1,89 @@
|
||||
# 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.
|
||||
|
||||
from functionaltests.api import base
|
||||
from functionaltests.api.v1.behaviors import quota_behaviors
|
||||
from functionaltests.api.v1.models import quota_models
|
||||
|
||||
|
||||
def get_set_project_quotas_request():
|
||||
return {"project_quotas":
|
||||
{"secrets": 50,
|
||||
"orders": 10,
|
||||
"containers": 20}}
|
||||
|
||||
|
||||
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')
|
||||
|
||||
def tearDown(self):
|
||||
super(QuotasTestCase, self).tearDown()
|
||||
|
||||
def test_get_quotas(self):
|
||||
"""Get quota information"""
|
||||
|
||||
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'])
|
||||
|
||||
def test_get_project_quota_list(self):
|
||||
"""Get list of all project quotas"""
|
||||
|
||||
resp, project_quotas_list = self.behaviors.get_project_quotas_list()
|
||||
|
||||
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):
|
||||
"""Get project quota information for specific project"""
|
||||
|
||||
resp = self.behaviors.get_project_quotas(self.project_id)
|
||||
|
||||
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):
|
||||
"""Get project quota information"""
|
||||
|
||||
request_model = quota_models.ProjectQuotaRequestModel(
|
||||
**get_set_project_quotas_request())
|
||||
resp = self.behaviors.set_project_quotas(self.project_id,
|
||||
request_model)
|
||||
self.assertEqual(200, resp.status_code)
|
||||
|
||||
def test_delete_project_quotas(self):
|
||||
"""Get project quota information"""
|
||||
|
||||
resp = self.behaviors.delete_project_quotas(self.project_id)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
@@ -0,0 +1,39 @@
|
||||
"""
|
||||
Copyright 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.
|
||||
"""
|
||||
|
||||
from functionaltests.api.v1.models.base_models import BaseModel
|
||||
|
||||
|
||||
class QuotaModel(BaseModel):
|
||||
|
||||
def __init__(self, quotas=None):
|
||||
super(QuotaModel, 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
|
||||
|
||||
|
||||
class ProjectQuotaRequestModel(BaseModel):
|
||||
|
||||
def __init__(self, project_quotas=None):
|
||||
super(ProjectQuotaRequestModel, self).__init__()
|
||||
self.project_quotas = project_quotas
|
||||
@@ -89,6 +89,10 @@ class FunctionalTestAuth(auth.AuthBase):
|
||||
"""Return the UID used by keystone to uniquely identify the user"""
|
||||
return self.authenticate()['user_id']
|
||||
|
||||
def get_project_id(self):
|
||||
"""Return the UID used by keystone to identify the user's project"""
|
||||
return self.authenticate()['project_id']
|
||||
|
||||
def __call__(self, r):
|
||||
creds = self.authenticate()
|
||||
|
||||
|
||||
@@ -221,7 +221,8 @@ class BarbicanClient(object):
|
||||
next_ref = item_list.get('next')
|
||||
elif 'previous' == item:
|
||||
prev_ref = item_list.get('previous')
|
||||
elif item in ('secrets', 'orders', 'containers', 'consumers'):
|
||||
elif item in ('secrets', 'orders', 'containers',
|
||||
'consumers', 'project_quotas'):
|
||||
for entity in item_list.get(item):
|
||||
models.append(model_type(**entity))
|
||||
|
||||
@@ -287,3 +288,9 @@ class BarbicanClient(object):
|
||||
return self._auth[user_name].get_user_id()
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_project_id_from_name(self, user_name):
|
||||
if user_name and self._auth[user_name]:
|
||||
return self._auth[user_name].get_project_id()
|
||||
else:
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user