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:
parent
6395765b82
commit
619b4753cd
196
api-ref/source/dns-api-v2-quota.inc
Normal file
196
api-ref/source/dns-api-v2-quota.inc
Normal 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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
7
api-ref/source/samples/quotas/get-quotas-response.json
Normal file
7
api-ref/source/samples/quotas/get-quotas-response.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"api_export_size": 1000,
|
||||
"recordset_records": 20,
|
||||
"zone_records": 500,
|
||||
"zone_recordsets": 500,
|
||||
"zones": 100
|
||||
}
|
3
api-ref/source/samples/quotas/set-quotas-request.json
Normal file
3
api-ref/source/samples/quotas/set-quotas-request.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"zones": 500
|
||||
}
|
7
api-ref/source/samples/quotas/set-quotas-response.json
Normal file
7
api-ref/source/samples/quotas/set-quotas-response.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"api_export_size": 1000,
|
||||
"recordset_records": 20,
|
||||
"zone_records": 500,
|
||||
"zone_recordsets": 500,
|
||||
"zones": 500
|
||||
}
|
@ -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)
|
||||
|
||||
|
78
designate/api/v2/controllers/quotas.py
Normal file
78
designate/api/v2/controllers/quotas.py
Normal 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 ''
|
@ -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()
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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):
|
||||
|
15
releasenotes/notes/v2-api-quotas-dd7e189cddcf7b96.yaml
Normal file
15
releasenotes/notes/v2-api-quotas-dd7e189cddcf7b96.yaml
Normal 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
|
Loading…
Reference in New Issue
Block a user