Add the ability to check the tenant quota in detail

Now the quota-set API only returned single attribute
'limit', this change intends to add a new API
'quota-sets/{project_id}/detail' to retrieve more info
with attributes 'in_use', 'limit', 'reserved'.

APIImpact
Implements: blueprint admin-check-tenant-quota-usage
Depends-On: Ie0eb7d32b7b032ffdb7f7dd47f68841211e7d7a6
Change-Id: I499b099a3ba7704a2108cd15f80ff507e24b7cd0
This commit is contained in:
zhongjun 2016-09-07 17:10:07 +08:00 committed by zhongjun2
parent f427dfe00b
commit 7275aafc9e
13 changed files with 217 additions and 5 deletions

View File

@ -2124,6 +2124,14 @@ quota_gigabytes:
in: body
required: true
type: integer
quota_gigabytes_detail:
description: |
The limit, in_use, reserved number of gigabytes allowed
for each tenant.
in: body
min_version: 2.25
required: true
type: object
quota_gigabytes_request:
description: |
The number of gigabytes for the tenant.
@ -2143,6 +2151,14 @@ quota_share_networks:
in: body
required: true
type: integer
quota_share_networks_detail:
description: |
The limit, in_use, reserved number of share networks
allowed for each tenant.
in: body
min_version: 2.25
required: true
type: object
quota_share_networks_request:
description: |
The number of share networks for the tenant.
@ -2155,6 +2171,14 @@ quota_shares:
in: body
required: true
type: integer
quota_shares_detail:
description: |
The limit, in_use, reserved number of shares allowed
for each tenant.
in: body
min_version: 2.25
required: true
type: object
quota_shares_request:
description: |
The number of shares for the tenant.
@ -2168,6 +2192,14 @@ quota_snapshot_gigabytes:
in: body
required: true
type: integer
quota_snapshot_gigabytes_detail:
description: |
The limit, in_use, reserved number of gigabytes for the
snapshots allowed for each tenant.
in: body
min_version: 2.25
required: true
type: object
quota_snapshot_gigabytes_request:
description: |
The number of gigabytes for the snapshots for the
@ -2181,6 +2213,14 @@ quota_snapshots:
in: body
required: true
type: integer
quota_snapshots_detail:
description: |
The limit, in_use, reserved number of snapshots allowed
for each tenant.
in: body
min_version: 2.25
required: true
type: object
quota_snapshots_request:
description: |
The number of snapshots for the tenant.

View File

@ -93,6 +93,49 @@ Response example
:language: javascript
Show quota set in detail
========================
.. rest_method:: GET /v2/{tenant_id}/quota-sets/{tenant_id}/detail?user_id={user_id}
Shows quotas for a tenant in detail.
If you specify the optional ``user_id`` query parameter, you get
the quotas for this user in the tenant. If you omit this parameter,
you get the quotas for the project.
Normal response codes: 200
Error response codes: badRequest(400), unauthorized(401), forbidden(403)
Request
-------
.. rest_parameters:: parameters.yaml
- tenant_id: tenant_id_path
- tenant_id: tenant_id
- user_id: user_id_query
Response parameters
-------------------
.. rest_parameters:: parameters.yaml
- quota_set: quota_set
- id: quota_tenant_id
- gigabytes: quota_gigabytes_detail
- snapshots: quota_snapshots_detail
- shares: quota_shares_detail
- snapshot_gigabytes: quota_snapshot_gigabytes_detail
- share_networks: quota_share_networks_detail
Response example
----------------
.. literalinclude:: samples/quota-show-detail-response.json
:language: javascript
Update quota set
================

View File

@ -0,0 +1,20 @@
{
"quota_set": {
"id": "16e1ab15c35a457e9c2b2aa189f544e1",
"gigabytes": {"in_use": 0,
"limit": 1000,
"reserved": 0},
"shares": {"in_use": 0,
"limit": 50,
"reserved": 0},
"snapshot_gigabytes": {"in_use": 0,
"limit": 1000,
"reserved": 0},
"snapshots": {"in_use": 0,
"limit": 50,
"reserved": 0},
"share_networks": {"in_use": 0,
"limit": 10,
"reserved": 0}
}
}

View File

@ -82,6 +82,7 @@ REST_API_VERSION_HISTORY = """
* 2.24 - Added optional create_share_from_snapshot_support extra spec,
which was previously inferred from the 'snapshot_support' extra
spec. Also made the 'snapshot_support' extra spec optional.
* 2.25 - Added quota-show detail API.
"""
@ -89,7 +90,7 @@ REST_API_VERSION_HISTORY = """
# The default api version request is defined to be the
# minimum version of the API supported.
_MIN_API_VERSION = "2.0"
_MAX_API_VERSION = "2.24"
_MAX_API_VERSION = "2.25"
DEFAULT_API_VERSION = _MIN_API_VERSION

View File

@ -147,3 +147,7 @@ user documentation.
----
Added optional create_share_from_snapshot_support extra spec. Made
snapshot_support extra spec optional.
2.25
----
Added quota-show detail API.

View File

@ -66,14 +66,18 @@ class QuotaSetsMixin(object):
return {k: v['limit'] for k, v in values.items()}
@wsgi.Controller.authorize("show")
def _show(self, req, id):
def _show(self, req, id, detail=False):
context = req.environ['manila.context']
params = parse.parse_qs(req.environ.get('QUERY_STRING', ''))
user_id = params.get('user_id', [None])[0]
try:
db.authorize_project_context(context, id)
# _get_quotas use 'usages' to indicate whether retrieve additional
# attributes, so pass detail to the argument.
return self._view_builder.detail_list(
self._get_quotas(context, id, user_id=user_id), id)
self._get_quotas(context, id, user_id=user_id,
usages=detail), id)
except exception.NotAuthorized:
raise webob.exc.HTTPForbidden()
@ -221,6 +225,10 @@ class QuotaSetsController(QuotaSetsMixin, wsgi.Controller):
def show(self, req, id):
return self._show(req, id)
@wsgi.Controller.api_version('2.25')
def detail(self, req, id):
return self._show(req, id, True)
@wsgi.Controller.api_version('2.7')
def defaults(self, req, id):
return self._defaults(req, id)

View File

@ -106,7 +106,8 @@ class APIRouter(manila.api.openstack.APIRouter):
mapper.resource("quota-set",
"quota-sets",
controller=self.resources["quota_sets"],
member={"defaults": "GET"})
member={"defaults": "GET",
"detail": "GET"})
self.resources["quota_class_sets_legacy"] = (
quota_class_sets.create_resource_legacy())

View File

@ -26,6 +26,7 @@ from oslo_config import cfg
import webob.exc
import webob.response
from manila.api.openstack import api_version_request as api_version
from manila.api.v2 import quota_sets
from manila import context
from manila import exception
@ -121,6 +122,48 @@ class QuotaSetsControllerTest(test.TestCase):
self.assertEqual(expected, result)
@ddt.data(REQ, REQ_WITH_USER)
def test_quota_detail(self, request):
request.api_version_request = api_version.APIVersionRequest('2.25')
quotas = {
"shares": 23,
"snapshots": 34,
"gigabytes": 45,
"snapshot_gigabytes": 56,
"share_networks": 67,
}
expected = {
'quota_set': {
'id': self.project_id,
'shares': {'in_use': 0,
'limit': quotas['shares'],
'reserved': 0},
'gigabytes': {'in_use': 0,
'limit': quotas['gigabytes'], 'reserved': 0},
'snapshots': {'in_use': 0,
'limit': quotas['snapshots'], 'reserved': 0},
'snapshot_gigabytes': {
'in_use': 0,
'limit': quotas['snapshot_gigabytes'],
'reserved': 0,
},
'share_networks': {
'in_use': 0,
'limit': quotas['share_networks'],
'reserved': 0
},
}
}
for k, v in quotas.items():
CONF.set_default('quota_' + k, v)
result = self.controller.detail(request, self.project_id)
self.assertEqual(expected, result)
self.mock_policy_check.assert_called_once_with(
request.environ['manila.context'], self.resource_name, 'show')
@ddt.data(REQ, REQ_WITH_USER)
def test_show_quota(self, request):
quotas = {

View File

@ -30,7 +30,7 @@ ShareGroup = [
help="The minimum api microversion is configured to be the "
"value of the minimum microversion supported by Manila."),
cfg.StrOpt("max_api_microversion",
default="2.24",
default="2.25",
help="The maximum api microversion is configured to be the "
"value of the latest microversion supported by Manila."),
cfg.StrOpt("region",

View File

@ -785,6 +785,17 @@ class SharesV2Client(shares_client.SharesClient):
self.expected_success(202, resp.status)
return body
def detail_quotas(self, tenant_id, user_id=None, url=None,
version=LATEST_MICROVERSION):
if url is None:
url = self._get_quotas_url(version)
url += '/%s/detail' % tenant_id
if user_id is not None:
url += "?user_id=%s" % user_id
resp, body = self.get(url, version=version)
self.expected_success(200, resp.status)
return self._parse_resp(body)
def update_quotas(self, tenant_id, user_id=None, shares=None,
snapshots=None, gigabytes=None, snapshot_gigabytes=None,
share_networks=None, force=True, url=None,

View File

@ -203,3 +203,15 @@ class SharesAdminQuotasNegativeTest(base.BaseSharesAdminTest):
self.shares_v2_client.tenant_id,
version=version, url=url,
)
@tc.attr(base.TAG_NEGATIVE, base.TAG_API)
def test_show_quota_detail_with_wrong_versions(self):
version = '2.24'
url = 'quota-sets'
self.assertRaises(
lib_exc.NotFound,
self.shares_v2_client.detail_quotas,
self.shares_v2_client.tenant_id,
version=version, url=url,
)

View File

@ -64,3 +64,28 @@ class SharesQuotasTest(base.BaseSharesTest):
self.assertGreater(int(quotas["shares"]), -2)
self.assertGreater(int(quotas["snapshots"]), -2)
self.assertGreater(int(quotas["share_networks"]), -2)
@tc.attr(base.TAG_POSITIVE, base.TAG_API)
@base.skip_if_microversion_not_supported("2.25")
def test_show_quotas_detail(self):
quotas = self.shares_v2_client.detail_quotas(self.tenant_id)
quota_keys = list(quotas.keys())
for outer in ('gigabytes', 'snapshot_gigabytes', 'shares',
'snapshots', 'share_networks'):
self.assertIn(outer, quota_keys)
for inner in ('in_use', 'limit', 'reserved'):
self.assertIn(inner, list(quotas[outer].keys()))
self.assertGreater(int(quotas[outer][inner]), -2)
@tc.attr(base.TAG_POSITIVE, base.TAG_API)
@base.skip_if_microversion_not_supported("2.25")
def test_show_quotas_detail_for_user(self):
quotas = self.shares_v2_client.detail_quotas(self.tenant_id,
self.user_id)
quota_keys = list(quotas.keys())
for outer in ('gigabytes', 'snapshot_gigabytes', 'shares',
'snapshots', 'share_networks'):
self.assertIn(outer, quota_keys)
for inner in ('in_use', 'limit', 'reserved'):
self.assertIn(inner, list(quotas[outer].keys()))
self.assertGreater(int(quotas[outer][inner]), -2)

View File

@ -0,0 +1,4 @@
---
features:
- Added detail API to show user and tenant specific usages through the
quota-sets resource.