diff --git a/manila/api/contrib/used_limits.py b/manila/api/contrib/used_limits.py deleted file mode 100644 index b1b0a75a57..0000000000 --- a/manila/api/contrib/used_limits.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright 2014 Mirantis Inc. -# -# 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 six - -from manila.api import extensions -from manila.api.openstack import wsgi -from manila import quota - -QUOTAS = quota.QUOTAS - -authorize = extensions.extension_authorizer('limits', 'used_limits') - - -class UsedLimitsController(wsgi.Controller): - - @wsgi.extends - def index(self, req, resp_obj): - context = req.environ['manila.context'] - authorize(context) - - quotas = QUOTAS.get_project_quotas(context, - context.project_id, - usages=True) - - quota_map = { - 'totalSharesUsed': 'shares', - 'totalShareSnapshotsUsed': 'snapshots', - 'totalShareNetworksUsed': 'share_networks', - 'totalShareGigabytesUsed': 'gigabytes', - 'totalSnapshotGigabytesUsed': 'snapshot_gigabytes', - } - - used_limits = {} - for display_name, quota_name in six.iteritems(quota_map): - if quota_name in quotas: - used_limits[display_name] = quotas[quota_name]['in_use'] - - resp_obj.obj['limits']['absolute'].update(used_limits) - - -class Used_limits(extensions.ExtensionDescriptor): - """Provide data on limited resources that are being used.""" - - name = "UsedLimits" - alias = 'os-used-limits' - updated = "2014-03-27T00:00:00+00:00" - - def get_controller_extensions(self): - controller = UsedLimitsController() - extension = extensions.ControllerExtension(self, 'limits', controller) - return [extension] diff --git a/manila/api/v1/limits.py b/manila/api/v1/limits.py index 153fc0786f..eaaee175f8 100644 --- a/manila/api/v1/limits.py +++ b/manila/api/v1/limits.py @@ -45,15 +45,18 @@ PER_HOUR = 60 * 60 PER_DAY = 60 * 60 * 24 -class LimitsController(object): +class LimitsController(wsgi.Controller): """Controller for accessing limits in the OpenStack API.""" def index(self, req): """Return all global and rate limit information.""" context = req.environ['manila.context'] quotas = QUOTAS.get_project_quotas(context, context.project_id, - usages=False) - abs_limits = dict((k, v['limit']) for k, v in quotas.items()) + usages=True) + abs_limits = {'in_use': {}, 'limit': {}} + for k, v in quotas.items(): + abs_limits['limit'][k] = v['limit'] + abs_limits['in_use'][k] = v['in_use'] rate_limits = req.environ.get("manila.limits", []) builder = self._get_view_builder(req) diff --git a/manila/api/views/limits.py b/manila/api/views/limits.py index 536baf368f..0d03ad9c85 100644 --- a/manila/api/views/limits.py +++ b/manila/api/views/limits.py @@ -16,7 +16,6 @@ import datetime from oslo_utils import timeutils -import six class ViewBuilder(object): @@ -36,24 +35,34 @@ class ViewBuilder(object): return output def _build_absolute_limits(self, absolute_limits): - """Builder for absolute limits + """Builder for absolute limits. absolute_limits should be given as a dict of limits. - For example: {"ram": 512, "gigabytes": 1024}. - + For example: {"limit": {"shares": 10, "gigabytes": 1024}, + "in_use": {"shares": 8, "gigabytes": 256}}. """ limit_names = { - "gigabytes": ["maxTotalShareGigabytes"], - "snapshot_gigabytes": ["maxTotalSnapshotGigabytes"], - "shares": ["maxTotalShares"], - "snapshots": ["maxTotalShareSnapshots"], - "share_networks": ["maxTotalShareNetworks"], + "limit": { + "gigabytes": ["maxTotalShareGigabytes"], + "snapshot_gigabytes": ["maxTotalSnapshotGigabytes"], + "shares": ["maxTotalShares"], + "snapshots": ["maxTotalShareSnapshots"], + "share_networks": ["maxTotalShareNetworks"], + }, + "in_use": { + "shares": ["totalSharesUsed"], + "snapshots": ["totalShareSnapshotsUsed"], + "share_networks": ["totalShareNetworksUsed"], + "gigabytes": ["totalShareGigabytesUsed"], + "snapshot_gigabytes": ["totalSnapshotGigabytesUsed"], + }, } limits = {} - for name, value in six.iteritems(absolute_limits): - if name in limit_names and value is not None: - for name in limit_names[name]: - limits[name] = value + for mapping_key in limit_names.keys(): + for k, v in absolute_limits.get(mapping_key, {}).items(): + if k in limit_names.get(mapping_key, []) and v is not None: + for name in limit_names[mapping_key][k]: + limits[name] = v return limits def _build_rate_limits(self, rate_limits): diff --git a/manila/tests/api/contrib/test_used_limits.py b/manila/tests/api/contrib/test_used_limits.py deleted file mode 100644 index 7649c5e49b..0000000000 --- a/manila/tests/api/contrib/test_used_limits.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright 2014 Mirantis Inc. -# All Rights Reserved. -# -# 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 mock -import six - -from manila.api.contrib import used_limits -from manila.api.openstack import wsgi -from manila import quota -from manila import test -from manila.tests.api import fakes - - -class FakeRequest(object): - def __init__(self, context): - self.environ = {'manila.context': context} - - -class UsedLimitsTestCase(test.TestCase): - - def setUp(self): - """Run before each test.""" - super(UsedLimitsTestCase, self).setUp() - self.controller = used_limits.UsedLimitsController() - - def test_used_limits(self): - fake_req = FakeRequest(fakes.FakeRequestContext('fake', 'fake')) - obj = {"limits": {"rate": [], "absolute": {}}} - res = wsgi.ResponseObject(obj) - quota_map = { - 'totalSharesUsed': 'shares', - 'totalShareSnapshotsUsed': 'snapshots', - 'totalShareNetworksUsed': 'share_networks', - 'totalShareGigabytesUsed': 'gigabytes', - } - limits = {} - for display_name, q in six.iteritems(quota_map): - limits[q] = {'limit': 2, 'in_use': 1, } - - def stub_get_project_quotas(*args, **kwargs): - return limits - - with mock.patch.object(quota.QUOTAS, 'get_project_quotas', - mock.Mock(side_effect=stub_get_project_quotas)): - - self.controller.index(fake_req, res) - abs_limits = res.obj['limits']['absolute'] - for used_limit, value in six.iteritems(abs_limits): - self.assertEqual(limits[quota_map[used_limit]]['in_use'], - value) diff --git a/manila/tests/api/v1/test_limits.py b/manila/tests/api/v1/test_limits.py index 339a0f3a57..1323982fa1 100644 --- a/manila/tests/api/v1/test_limits.py +++ b/manila/tests/api/v1/test_limits.py @@ -32,9 +32,9 @@ from manila import test TEST_LIMITS = [ limits.Limit("GET", "/delayed", "^/delayed", 1, limits.PER_MINUTE), limits.Limit("POST", "*", ".*", 7, limits.PER_MINUTE), - limits.Limit("POST", "/volumes", "^/volumes", 3, limits.PER_MINUTE), + limits.Limit("POST", "/shares", "^/shares", 3, limits.PER_MINUTE), limits.Limit("PUT", "*", "", 10, limits.PER_MINUTE), - limits.Limit("PUT", "/volumes", "^/volumes", 5, limits.PER_MINUTE), + limits.Limit("PUT", "/shares", "^/shares", 5, limits.PER_MINUTE), ] NS = { 'atom': 'http://www.w3.org/2005/Atom', @@ -52,8 +52,13 @@ class BaseLimitTestSuite(test.TestCase): self.absolute_limits = {} def stub_get_project_quotas(context, project_id, usages=True): - return dict((k, dict(limit=v)) - for k, v in self.absolute_limits.items()) + quotas = {} + for mapping_key in ('limit', 'in_use'): + for k, v in self.absolute_limits.get(mapping_key, {}).items(): + if k not in quotas: + quotas[k] = {} + quotas[k].update({mapping_key: v}) + return quotas self.mock_object(manila.quota.QUOTAS, "get_project_quotas", stub_get_project_quotas) @@ -112,9 +117,20 @@ class LimitsControllerTest(BaseLimitTestSuite): request = self._get_index_request() request = self._populate_limits(request) self.absolute_limits = { - 'gigabytes': 512, - 'shares': 5, - 'snapshots': 5 + 'limit': { + 'shares': 11, + 'gigabytes': 22, + 'snapshots': 33, + 'snapshot_gigabytes': 44, + 'share_networks': 55, + }, + 'in_use': { + 'shares': 3, + 'gigabytes': 4, + 'snapshots': 5, + 'snapshot_gigabytes': 6, + 'share_networks': 7, + }, } response = request.get_response(self.controller) expected = { @@ -155,10 +171,18 @@ class LimitsControllerTest(BaseLimitTestSuite): }, ], - "absolute": {"maxTotalShareGigabytes": 512, - "maxTotalShares": 5, - "maxTotalShareSnapshots": 5, - }, + "absolute": { + "totalSharesUsed": 3, + "totalShareGigabytesUsed": 4, + "totalShareSnapshotsUsed": 5, + "totalSnapshotGigabytesUsed": 6, + "totalShareNetworksUsed": 7, + "maxTotalShares": 11, + "maxTotalShareGigabytes": 22, + "maxTotalShareSnapshots": 33, + "maxTotalSnapshotGigabytes": 44, + "maxTotalShareNetworks": 55, + }, }, } body = jsonutils.loads(response.body) @@ -222,7 +246,10 @@ class LimitsControllerTest(BaseLimitTestSuite): self.assertEqual(expected, body['limits']['absolute']) def test_index_ignores_extra_absolute_limits_json(self): - self.absolute_limits = {'unknown_limit': 9001} + self.absolute_limits = { + 'in_use': {'unknown_limit': 9000}, + 'limit': {'unknown_limit': 9001}, + } self._test_index_absolute_limits_json({}) @@ -450,7 +477,7 @@ class LimiterTest(BaseLimitTestSuite): """ # First 6 requests on PUT /volumes expected = [None] * 5 + [12.0] - results = list(self._check(6, "PUT", "/volumes")) + results = list(self._check(6, "PUT", "/shares")) self.assertEqual(expected, results) # Next 5 request on PUT /anything @@ -734,47 +761,74 @@ class LimitsViewBuilderTest(test.TestCase): "remaining": 2, "unit": "MINUTE", "resetTime": 1311272226}, - {"URI": "*/volumes", - "regex": "^/volumes", + {"URI": "*/shares", + "regex": "^/shares", "value": 50, "verb": "POST", "remaining": 10, "unit": "DAY", "resetTime": 1311272226}] - self.absolute_limits = {"shares": 1, - "gigabytes": 5, - "snapshots": 5} + self.absolute_limits = { + "limit": { + "shares": 111, + "gigabytes": 222, + "snapshots": 333, + "snapshot_gigabytes": 444, + "share_networks": 555, + }, + "in_use": { + "shares": 65, + "gigabytes": 76, + "snapshots": 87, + "snapshot_gigabytes": 98, + "share_networks": 107, + }, + } def test_build_limits(self): tdate = "2011-07-21T18:17:06Z" - expected_limits = \ - {"limits": {"rate": [{"uri": "*", - "regex": ".*", - "limit": [{"value": 10, - "verb": "POST", - "remaining": 2, - "unit": "MINUTE", - "next-available": tdate}]}, - {"uri": "*/volumes", - "regex": "^/volumes", - "limit": [{"value": 50, - "verb": "POST", - "remaining": 10, - "unit": "DAY", - "next-available": tdate}]}], - "absolute": {"maxTotalShareGigabytes": 5, - "maxTotalShares": 1, - "maxTotalShareSnapshots": 5}}} + expected_limits = { + "limits": { + "rate": [ + {"uri": "*", + "regex": ".*", + "limit": [{"value": 10, + "verb": "POST", + "remaining": 2, + "unit": "MINUTE", + "next-available": tdate}]}, + {"uri": "*/shares", + "regex": "^/shares", + "limit": [{"value": 50, + "verb": "POST", + "remaining": 10, + "unit": "DAY", + "next-available": tdate}]} + ], + "absolute": { + "totalSharesUsed": 65, + "totalShareGigabytesUsed": 76, + "totalShareSnapshotsUsed": 87, + "totalSnapshotGigabytesUsed": 98, + "totalShareNetworksUsed": 107, + "maxTotalShares": 111, + "maxTotalShareGigabytes": 222, + "maxTotalShareSnapshots": 333, + "maxTotalSnapshotGigabytes": 444, + "maxTotalShareNetworks": 555, + } + } + } output = self.view_builder.build(self.rate_limits, self.absolute_limits) - self.assertDictMatch(output, expected_limits) + self.assertDictMatch(expected_limits, output) def test_build_limits_empty_limits(self): - expected_limits = {"limits": {"rate": [], - "absolute": {}}} - + expected_limits = {"limits": {"rate": [], "absolute": {}}} abs_limits = {} rate_limits = [] + output = self.view_builder.build(rate_limits, abs_limits) - self.assertDictMatch(output, expected_limits) + + self.assertDictMatch(expected_limits, output) diff --git a/manila/tests/policy.json b/manila/tests/policy.json index 81600eef02..63f5f70fef 100644 --- a/manila/tests/policy.json +++ b/manila/tests/policy.json @@ -63,8 +63,6 @@ "security_service:index": "", "security_service:get_all_security_services": "rule:admin_api", - "limits_extension:used_limits": "", - "scheduler_stats:pools:index": "rule:admin_api", "scheduler_stats:pools:detail": "rule:admin_api", diff --git a/manila_tempest_tests/tests/api/test_limits.py b/manila_tempest_tests/tests/api/test_limits.py index 044d6010c5..5046b30556 100644 --- a/manila_tempest_tests/tests/api/test_limits.py +++ b/manila_tempest_tests/tests/api/test_limits.py @@ -35,10 +35,12 @@ class ShareLimitsTest(base.BaseSharesTest): "maxTotalShares", "maxTotalShareSnapshots", "maxTotalShareNetworks", + "maxTotalSnapshotGigabytes", "totalSharesUsed", "totalShareSnapshotsUsed", "totalShareNetworksUsed", "totalShareGigabytesUsed", + "totalSnapshotGigabytesUsed", ] [self.assertIn(key, limits["absolute"].keys()) for key in abs_keys] @@ -54,7 +56,9 @@ class ShareLimitsTest(base.BaseSharesTest): self.assertGreater(int(abs_l["maxTotalShares"]), -2) self.assertGreater(int(abs_l["maxTotalShareSnapshots"]), -2) self.assertGreater(int(abs_l["maxTotalShareNetworks"]), -2) + self.assertGreater(int(abs_l["maxTotalSnapshotGigabytes"]), -2) self.assertGreater(int(abs_l["totalSharesUsed"]), -2) self.assertGreater(int(abs_l["totalShareSnapshotsUsed"]), -2) self.assertGreater(int(abs_l["totalShareNetworksUsed"]), -2) self.assertGreater(int(abs_l["totalShareGigabytesUsed"]), -2) + self.assertGreater(int(abs_l["totalSnapshotGigabytesUsed"]), -2)