Add v2/quotas

This adds the quotas api from /admin to /v2 with some changes.

All users can GET /v2/quotas/<project_id>

Users with "All-Projects" permission can view other projects
(by setting X-Auth-All-Projects:True in the HTTP Headers)

Users with "All-Projects" and "set-quotas" can set other
projects quotas

Moved the API rendering to Designate Object based rendering

Change-Id: I7a0b828824ad6f274d922748f5f9a68157cd939a
Depends-On: I06180a7402fc45940d4b312666cf2dfd33af1305
This commit is contained in:
Graham Hayes 2016-06-28 16:11:39 +01:00
parent 6395765b82
commit 619b4753cd
16 changed files with 385 additions and 18 deletions

View File

@ -0,0 +1,196 @@
======
Quotas
======
Quota operations.
View Quotas
===========
.. rest_method:: GET /v2/quotas/{project_id}
View a projects quotas
This returns a key:value set of quotas on the system.
.. note::
If a user is viewing another projects quotas, they will need to set
``x-auth-all-projects`` to ``True``
They will need a role with the ``All-Projects`` permission to do this.
Normal response codes: 200
Error response codes: 409,405,404,403,401,400,503
Request
-------
.. rest_parameters:: parameters.yaml
- x-auth-token: x-auth-token
- x-auth-all-projects: x-auth-all-projects
- x-auth-sudo-project-id: x-auth-sudo-project-id
- project_id: path_project_id
Response Parameters
-------------------
.. rest_parameters:: parameters.yaml
- x-openstack-request-id: x-openstack-request-id
Response Example
----------------
.. literalinclude:: samples/quotas/get-quotas-response.json
:language: javascript
View Current Project's Quotas
=============================
.. rest_method:: GET /v2/quotas/
View the quotas for the current project
This returns a key:value set of quotas on the system.
Normal response codes: 200
Error response codes: 409,405,404,403,401,400,503
Request
-------
.. rest_parameters:: parameters.yaml
- x-auth-token: x-auth-token
- x-auth-all-projects: x-auth-all-projects
- x-auth-sudo-project-id: x-auth-sudo-project-id
- project_id: path_project_id
Response Parameters
-------------------
.. rest_parameters:: parameters.yaml
- x-openstack-request-id: x-openstack-request-id
Response Example
----------------
.. literalinclude:: samples/quotas/get-quotas-response.json
:language: javascript
Set Quotas
==========
.. rest_method:: PATCH /v2/quotas/{project_id}
Set a projects quotas
The request should be a key:value set of quotas to be set
This returns a key:value set of quotas on the system.
.. note::
If a user is updating another projects quotas, they will need to set
``x-auth-all-projects`` to ``True``
They will need a role with the "All-Projects" and "set-quotas"
permission to do this.
Normal response codes: 200
Error response codes: 409,405,404,403,401,400,503
Request Example
---------------
.. literalinclude:: samples/quotas/set-quotas-request.json
:language: javascript
Request
-------
.. rest_parameters:: parameters.yaml
- x-auth-token: x-auth-token
- x-auth-all-projects: x-auth-all-projects
- x-auth-sudo-project-id: x-auth-sudo-project-id
- project_id: path_project_id
Response Parameters
-------------------
.. rest_parameters:: parameters.yaml
- x-openstack-request-id: x-openstack-request-id
Response Example
----------------
.. literalinclude:: samples/quotas/set-quotas-response.json
:language: javascript
Reset Quotas
============
.. rest_method:: DELETE /v2/quotas/{project_id}
Reset all quotas for a project to default
.. note::
If a user is resetting another projects quotas, they will need to set
``x-auth-all-projects`` to ``True``
They will need a role with the ``All-Projects`` and "set-quotas"
permission to do this.
Normal response codes: 204
Error response codes: 409,405,404,403,401,400,503
Request
-------
.. rest_parameters:: parameters.yaml
- x-auth-token: x-auth-token
- x-auth-all-projects: x-auth-all-projects
- x-auth-sudo-project-id: x-auth-sudo-project-id
- project_id: path_project_id
Response Parameters
-------------------
.. rest_parameters:: parameters.yaml
- x-openstack-request-id: x-openstack-request-id

View File

@ -19,3 +19,4 @@
.. include:: dns-api-v2-tld.inc
.. include:: dns-api-v2-tsigkey.inc
.. include:: dns-api-v2-blacklist.inc
.. include:: dns-api-v2-quota.inc

View File

@ -64,6 +64,13 @@ path_pool_id:
required: true
type: uuid
path_project_id:
description: |
ID for the project
in: path
required: true
type: uuid
path_recordset_id:
description: |
ID for the recordset

View File

@ -0,0 +1,7 @@
{
"api_export_size": 1000,
"recordset_records": 20,
"zone_records": 500,
"zone_recordsets": 500,
"zones": 100
}

View File

@ -0,0 +1,3 @@
{
"zones": 500
}

View File

@ -0,0 +1,7 @@
{
"api_export_size": 1000,
"recordset_records": 20,
"zone_records": 500,
"zone_recordsets": 500,
"zones": 500
}

View File

@ -35,6 +35,7 @@ class QuotasController(rest.RestController):
def get_one(self, tenant_id):
request = pecan.request
context = pecan.request.environ['context']
context.all_tenants = True
quotas = self.central_api.get_quotas(context, tenant_id)
@ -46,6 +47,7 @@ class QuotasController(rest.RestController):
request = pecan.request
response = pecan.response
context = request.environ['context']
context.all_tenants = True
body = request.body_dict
# Validate the request conforms to the schema
@ -69,6 +71,7 @@ class QuotasController(rest.RestController):
request = pecan.request
response = pecan.response
context = request.environ['context']
context.all_tenants = True
self.central_api.reset_quotas(context, tenant_id)

View File

@ -0,0 +1,78 @@
# COPYRIGHT 2014 Rackspace
#
# Author: Tim Simmons <tim.simmons@rackspace.com>
#
# 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 oslo_log import log as logging
from designate.api.v2.controllers import rest
from designate.objects.adapters import DesignateAdapter
from designate.objects import QuotaList
LOG = logging.getLogger(__name__)
class QuotasController(rest.RestController):
@pecan.expose(template='json:', content_type='application/json')
def get_all(self):
context = pecan.request.environ['context']
quotas = self.central_api.get_quotas(context, context.tenant)
quotas = QuotaList.from_dict(quotas)
return DesignateAdapter.render('API_v2', quotas)
@pecan.expose(template='json:', content_type='application/json')
def get_one(self, tenant_id):
context = pecan.request.environ['context']
quotas = self.central_api.get_quotas(context, tenant_id)
quotas = QuotaList.from_dict(quotas)
return DesignateAdapter.render('API_v2', quotas)
@pecan.expose(template='json:', content_type='application/json')
def patch_one(self, tenant_id):
"""Modify a Quota"""
request = pecan.request
context = request.environ['context']
body = request.body_dict
quotas = DesignateAdapter.parse('API_v2', body, QuotaList())
for quota in quotas:
self.central_api.set_quota(context, tenant_id, quota.resource,
quota.hard_limit)
quotas = self.central_api.get_quotas(context, tenant_id)
quotas = QuotaList.from_dict(quotas)
return DesignateAdapter.render('API_v2', quotas)
@pecan.expose(template=None, content_type='application/json')
def delete_one(self, tenant_id):
"""Reset to the Default Quotas"""
request = pecan.request
response = pecan.response
context = request.environ['context']
self.central_api.reset_quotas(context, tenant_id)
response.status_int = 204
return ''

View File

@ -27,6 +27,7 @@ from designate.api.v2.controllers import service_status
from designate.api.v2.controllers import zones
from designate.api.v2.controllers import tsigkeys
from designate.api.v2.controllers import recordsets
from designate.api.v2.controllers import quotas
LOG = logging.getLogger(__name__)
@ -62,3 +63,4 @@ class RootController(object):
service_statuses = service_status.ServiceStatusController()
tsigkeys = tsigkeys.TsigKeysController()
recordsets = recordsets.RecordSetsViewController()
quotas = quotas.QuotasController()

View File

@ -643,8 +643,8 @@ class Service(service.RPCService, service.Service):
target = {'tenant_id': tenant_id}
policy.check('get_quotas', context, target)
# This allows admins to get quota information correctly for all tenants
context.all_tenants = True
if tenant_id != context.tenant and not context.all_tenants:
raise exceptions.Forbidden()
return self.quota.get_quotas(context, tenant_id)
@ -663,6 +663,8 @@ class Service(service.RPCService, service.Service):
}
policy.check('set_quota', context, target)
if tenant_id != context.tenant and not context.all_tenants:
raise exceptions.Forbidden()
return self.quota.set_quota(context, tenant_id, resource, hard_limit)

View File

@ -24,22 +24,12 @@ class QuotaAPIv2Adapter(base.APIv2Adapter):
MODIFICATIONS = {
'fields': {
'zones': {
'rename': 'domains',
'resource': {
'read_only': False
},
'zone_records': {
'rename': 'domain_records',
'hard_limit': {
'read_only': False
},
'zone_recordsets': {
'rename': 'domain_recordsets',
'read_only': False
},
'recordset_records': {
'read_only': False
},
},
'options': {
'links': True,
@ -60,3 +50,30 @@ class QuotaListAPIv2Adapter(base.APIv2Adapter):
'collection_name': 'quotas',
}
}
@classmethod
def _render_list(cls, list_object, *args, **kwargs):
r_list = {}
for object in list_object:
r_list[object.resource] = object.hard_limit
return r_list
@classmethod
def _parse_list(cls, values, output_object, *args, **kwargs):
for key, value in values.items():
# Add the object to the list
output_object.append(
cls.ADAPTER_OBJECT.LIST_ITEM_TYPE.from_dict(
{
'resource': key,
'hard_limit': value,
}
)
)
# Return the filled list
return output_object

View File

@ -24,9 +24,31 @@ class Quota(base.DictObjectMixin, base.PersistentObjectMixin,
}
STRING_KEYS = [
'id', 'resource', 'tenant_id', 'hard_limit'
'resource', 'tenant_id', 'hard_limit'
]
class QuotaList(base.ListObjectMixin, base.DesignateObject):
LIST_ITEM_TYPE = Quota
@classmethod
def from_dict(cls, _dict):
instance = cls()
for field, value in _dict.items():
item = cls.LIST_ITEM_TYPE()
item.resource = field
item.hard_limit = value
instance.append(item)
return instance
def to_dict(self):
_dict = {}
for quota in self.objects:
_dict[quota.resource] = quota.hard_limit
return _dict

View File

@ -161,6 +161,8 @@ function configure_designate_tempest() {
iniset $TEMPEST_CONFIG dns_feature_enabled api_v2 $DESIGNATE_ENABLE_API_V2
iniset $TEMPEST_CONFIG dns_feature_enabled api_admin $DESIGNATE_ENABLE_API_ADMIN
iniset $TEMPEST_CONFIG dns_feature_enabled api_v2_root_recordsets True
iniset $TEMPEST_CONFIG dns_feature_enabled api_v2_quotas True
iniset $TEMPEST_CONFIG dns_feature_enabled bug_1573141_fixed True
# Tell tempest where are nameservers are.
nameservers=$DESIGNATE_SERVICE_HOST:$DESIGNATE_SERVICE_PORT_DNS

View File

@ -36,7 +36,10 @@ class DesignateV2Test(BaseDesignateTest):
'zones': 9999999,
'recordset_records': 9999999,
'zone_records': 9999999,
'zone_recordsets': 9999999}}))
'zone_recordsets': 9999999
}
})
)
def ensure_tld_exists(self, tld='com'):
if cfg.CONF.testconfig.no_admin_setup:

View File

@ -32,8 +32,10 @@ class QuotasClient(ClientMixin):
return self.deserialize(resp, body, QuotasModel)
def patch_quotas(self, tenant_id, quotas_model, **kwargs):
resp, body = self.client.patch(self.quotas_uri(tenant_id),
body=quotas_model.to_json(), **kwargs)
resp, body = self.client.patch(
self.quotas_uri(tenant_id),
body=quotas_model.to_json(),
**kwargs)
return self.deserialize(resp, body, QuotasModel)
def delete_quotas(self, tenant_id, **kwargs):

View File

@ -0,0 +1,15 @@
---
features:
- This adds the quotas api from /admin to /v2 with some changes.
All users can GET /v2/quotas/<project_id>
Users with "All-Projects" permission can view other projects
(by setting X-Auth-All-Projects:True in the HTTP Headers)
Users with "All-Projects" and "set-quotas" can set other
projects quotas
Moved the API rendering to Designate Object based rendering
fixes:
- V1 API Users can now query v1/quotas/<project_id> for quotas