From 7275aafc9e10a0fc8abcabdda08b291050090ef4 Mon Sep 17 00:00:00 2001 From: zhongjun Date: Wed, 7 Sep 2016 17:10:07 +0800 Subject: [PATCH] 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 --- api-ref/source/parameters.yaml | 40 +++++++++++++++++ api-ref/source/quota-sets.inc | 43 +++++++++++++++++++ .../samples/quota-show-detail-response.json | 20 +++++++++ manila/api/openstack/api_version_request.py | 3 +- .../openstack/rest_api_version_history.rst | 4 ++ manila/api/v2/quota_sets.py | 12 +++++- manila/api/v2/router.py | 3 +- manila/tests/api/v2/test_quota_sets.py | 43 +++++++++++++++++++ manila_tempest_tests/config.py | 2 +- .../services/share/v2/json/shares_client.py | 11 +++++ .../tests/api/admin/test_quotas_negative.py | 12 ++++++ manila_tempest_tests/tests/api/test_quotas.py | 25 +++++++++++ ...-tenant-quota-usages-7fs17djahy61nsd6.yaml | 4 ++ 13 files changed, 217 insertions(+), 5 deletions(-) create mode 100644 api-ref/source/samples/quota-show-detail-response.json create mode 100644 releasenotes/notes/add-ability-to-check-tenant-quota-usages-7fs17djahy61nsd6.yaml diff --git a/api-ref/source/parameters.yaml b/api-ref/source/parameters.yaml index 3b5537ad5f..8e5f8f0ce4 100644 --- a/api-ref/source/parameters.yaml +++ b/api-ref/source/parameters.yaml @@ -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. diff --git a/api-ref/source/quota-sets.inc b/api-ref/source/quota-sets.inc index 9992675e2f..50b35f747e 100644 --- a/api-ref/source/quota-sets.inc +++ b/api-ref/source/quota-sets.inc @@ -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 ================ diff --git a/api-ref/source/samples/quota-show-detail-response.json b/api-ref/source/samples/quota-show-detail-response.json new file mode 100644 index 0000000000..d29274d3e8 --- /dev/null +++ b/api-ref/source/samples/quota-show-detail-response.json @@ -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} + } +} diff --git a/manila/api/openstack/api_version_request.py b/manila/api/openstack/api_version_request.py index 3556653377..59f8baef7e 100644 --- a/manila/api/openstack/api_version_request.py +++ b/manila/api/openstack/api_version_request.py @@ -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 diff --git a/manila/api/openstack/rest_api_version_history.rst b/manila/api/openstack/rest_api_version_history.rst index 4de289c2e7..cc406fd33e 100644 --- a/manila/api/openstack/rest_api_version_history.rst +++ b/manila/api/openstack/rest_api_version_history.rst @@ -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. diff --git a/manila/api/v2/quota_sets.py b/manila/api/v2/quota_sets.py index ab1d65d24a..f11b059f54 100644 --- a/manila/api/v2/quota_sets.py +++ b/manila/api/v2/quota_sets.py @@ -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) diff --git a/manila/api/v2/router.py b/manila/api/v2/router.py index 9302a98729..d7bc97c3e2 100644 --- a/manila/api/v2/router.py +++ b/manila/api/v2/router.py @@ -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()) diff --git a/manila/tests/api/v2/test_quota_sets.py b/manila/tests/api/v2/test_quota_sets.py index 1f5ddd96f7..9cfa2e9548 100644 --- a/manila/tests/api/v2/test_quota_sets.py +++ b/manila/tests/api/v2/test_quota_sets.py @@ -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 = { diff --git a/manila_tempest_tests/config.py b/manila_tempest_tests/config.py index 7d7f3fbb45..1cd553edc2 100644 --- a/manila_tempest_tests/config.py +++ b/manila_tempest_tests/config.py @@ -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", diff --git a/manila_tempest_tests/services/share/v2/json/shares_client.py b/manila_tempest_tests/services/share/v2/json/shares_client.py index de8c62ed82..cbc26b5063 100644 --- a/manila_tempest_tests/services/share/v2/json/shares_client.py +++ b/manila_tempest_tests/services/share/v2/json/shares_client.py @@ -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, diff --git a/manila_tempest_tests/tests/api/admin/test_quotas_negative.py b/manila_tempest_tests/tests/api/admin/test_quotas_negative.py index d6c47e5398..b8557e14ac 100644 --- a/manila_tempest_tests/tests/api/admin/test_quotas_negative.py +++ b/manila_tempest_tests/tests/api/admin/test_quotas_negative.py @@ -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, + ) diff --git a/manila_tempest_tests/tests/api/test_quotas.py b/manila_tempest_tests/tests/api/test_quotas.py index e9f5042a51..a7540ed8a1 100644 --- a/manila_tempest_tests/tests/api/test_quotas.py +++ b/manila_tempest_tests/tests/api/test_quotas.py @@ -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) diff --git a/releasenotes/notes/add-ability-to-check-tenant-quota-usages-7fs17djahy61nsd6.yaml b/releasenotes/notes/add-ability-to-check-tenant-quota-usages-7fs17djahy61nsd6.yaml new file mode 100644 index 0000000000..9f1c373f2b --- /dev/null +++ b/releasenotes/notes/add-ability-to-check-tenant-quota-usages-7fs17djahy61nsd6.yaml @@ -0,0 +1,4 @@ +--- +features: + - Added detail API to show user and tenant specific usages through the + quota-sets resource.