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:
Valeriy Ponomaryov 2015-10-19 14:59:08 +03:00
parent e3943acf73
commit 30d9144aac
7 changed files with 127 additions and 184 deletions

View File

@ -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]

View File

@ -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)

View File

@ -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):

View File

@ -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)

View File

@ -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)

View File

@ -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",

View File

@ -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)