Port used limits to core API
Extension 'used limits' was extending core 'limits' API with used resources information. So, provide such information in core API and remove extension completely. It does not require neither update of API router nor bump of microversion. Partially implements bp ext-to-core Change-Id: I0ff71fbd0281eb2e34a9acbfc72ff8c14390e8d9
This commit is contained in:
parent
e3943acf73
commit
30d9144aac
@ -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]
|
@ -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)
|
||||
|
@ -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):
|
||||
|
@ -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)
|
@ -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)
|
||||
|
@ -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",
|
||||
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user