Fix limits v3 follow API v3 rules

* Add expected_errors decorator.
* Remove 'absolute_limit' from response, that already return by
extension quota_sets. And update api samples.

Partially implements bp nova-v3-api

Change-Id: Iea3ff494b8abed77fdc4b9e5e552784ed3733c34
This commit is contained in:
He Jie Xu
2013-08-02 15:19:51 +08:00
parent 7af1f66363
commit a9e29b7e9f
8 changed files with 43 additions and 235 deletions

View File

@@ -1,18 +1,5 @@
{ {
"limits": { "limits": {
"absolute": {
"maxImageMeta": 128,
"maxPersonality": 5,
"maxPersonalitySize": 10240,
"maxSecurityGroupRules": 20,
"maxSecurityGroups": 10,
"maxServerMeta": 128,
"maxTotalCores": 20,
"maxTotalFloatingIps": 10,
"maxTotalInstances": 10,
"maxTotalKeypairs": 100,
"maxTotalRAMSize": 51200
},
"rate": [ "rate": [
{ {
"limit": [ "limit": [

View File

@@ -13,17 +13,4 @@
<limit next-available="2013-09-09T13:37:32Z" unit="MINUTE" verb="GET" remaining="3" value="3"/> <limit next-available="2013-09-09T13:37:32Z" unit="MINUTE" verb="GET" remaining="3" value="3"/>
</rate> </rate>
</rates> </rates>
<absolute>
<limit name="maxServerMeta" value="128"/>
<limit name="maxPersonality" value="5"/>
<limit name="maxImageMeta" value="128"/>
<limit name="maxPersonalitySize" value="10240"/>
<limit name="maxSecurityGroupRules" value="20"/>
<limit name="maxTotalKeypairs" value="100"/>
<limit name="maxSecurityGroups" value="10"/>
<limit name="maxTotalCores" value="20"/>
<limit name="maxTotalFloatingIps" value="10"/>
<limit name="maxTotalInstances" value="10"/>
<limit name="maxTotalRAMSize" value="51200"/>
</absolute>
</limits> </limits>

View File

@@ -61,53 +61,21 @@ class LimitsTemplate(xmlutil.TemplateBuilder):
limit.set('unit', 'unit') limit.set('unit', 'unit')
limit.set('next-available', 'next-available') limit.set('next-available', 'next-available')
absolute = xmlutil.SubTemplateElement(root, 'absolute',
selector='absolute')
limit = xmlutil.SubTemplateElement(absolute, 'limit',
selector=xmlutil.get_items)
limit.set('name', 0)
limit.set('value', 1)
return xmlutil.MasterTemplate(root, 1, nsmap=limits_nsmap) return xmlutil.MasterTemplate(root, 1, nsmap=limits_nsmap)
class LimitsController(wsgi.Controller): class LimitsController(wsgi.Controller):
"""Controller for accessing limits in the OpenStack API.""" """Controller for accessing limits in the OpenStack API."""
@extensions.expected_errors(())
@wsgi.serializers(xml=LimitsTemplate) @wsgi.serializers(xml=LimitsTemplate)
def index(self, req): def index(self, req):
"""Return all global and rate limit information.""" """Return all global and rate limit information."""
context = req.environ['nova.context'] context = req.environ['nova.context']
quotas = QUOTAS.get_project_quotas(context, context.project_id,
usages=False)
abs_limits = dict((k, v['limit']) for k, v in quotas.items())
rate_limits = req.environ.get("nova.limits", []) rate_limits = req.environ.get("nova.limits", [])
builder = self._get_view_builder(req) builder = limits_views.ViewBuilderV3()
return builder.build(rate_limits, abs_limits) return builder.build(rate_limits)
def create(self, req, body):
"""Create a new limit."""
raise webob.exc.HTTPNotImplemented()
def delete(self, req, id):
"""Delete the limit."""
raise webob.exc.HTTPNotImplemented()
def detail(self, req):
"""Return limit details."""
raise webob.exc.HTTPNotImplemented()
def show(self, req, id):
"""Show limit information."""
raise webob.exc.HTTPNotImplemented()
def update(self, req, id, body):
"""Update existing limit."""
raise webob.exc.HTTPNotImplemented()
def _get_view_builder(self, req):
return limits_views.ViewBuilder()
class Limit(object): class Limit(object):

View File

@@ -0,0 +1,20 @@
<element name="limits" ns="http://docs.openstack.org/common/api/v1.0"
xmlns="http://relaxng.org/ns/structure/1.0">
<element name="rates">
<zeroOrMore>
<element name="rate">
<attribute name="uri"> <text/> </attribute>
<attribute name="regex"> <text/> </attribute>
<zeroOrMore>
<element name="limit">
<attribute name="value"> <text/> </attribute>
<attribute name="verb"> <text/> </attribute>
<attribute name="remaining"> <text/> </attribute>
<attribute name="unit"> <text/> </attribute>
<attribute name="next-available"> <text/> </attribute>
</element>
</zeroOrMore>
</element>
</zeroOrMore>
</element>
</element>

View File

@@ -98,3 +98,10 @@ class ViewBuilder(object):
"unit": rate_limit["unit"], "unit": rate_limit["unit"],
"next-available": timeutils.isotime(at=next_avail), "next-available": timeutils.isotime(at=next_avail),
} }
class ViewBuilderV3(ViewBuilder):
def build(self, rate_limits):
rate_limits = self._build_rate_limits(rate_limits)
return {"limits": {"rate": rate_limits}}

View File

@@ -30,7 +30,6 @@ from nova.api.openstack import xmlutil
import nova.context import nova.context
from nova.openstack.common import jsonutils from nova.openstack.common import jsonutils
from nova import test from nova import test
from nova.tests.api.openstack import fakes
from nova.tests import matchers from nova.tests import matchers
from nova import utils from nova import utils
@@ -58,14 +57,6 @@ class BaseLimitTestSuite(test.NoDBTestCase):
super(BaseLimitTestSuite, self).setUp() super(BaseLimitTestSuite, self).setUp()
self.time = 0.0 self.time = 0.0
self.stubs.Set(limits.Limit, "_get_time", self._get_time) self.stubs.Set(limits.Limit, "_get_time", self._get_time)
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())
self.stubs.Set(nova.quota.QUOTAS, "get_project_quotas",
stub_get_project_quotas)
def _get_time(self): def _get_time(self):
"""Return the "time" according to this test suite.""" """Return the "time" according to this test suite."""
@@ -111,8 +102,7 @@ class LimitsControllerTest(BaseLimitTestSuite):
body = self.controller.index(request) body = self.controller.index(request)
expected = { expected = {
"limits": { "limits": {
"rate": [], "rate": []
"absolute": {},
}, },
} }
self.assertEqual(expected, body) self.assertEqual(expected, body)
@@ -121,15 +111,6 @@ class LimitsControllerTest(BaseLimitTestSuite):
# Test getting limit details in JSON. # Test getting limit details in JSON.
request = self._get_index_request() request = self._get_index_request()
request = self._populate_limits(request) request = self._populate_limits(request)
self.absolute_limits = {
'ram': 512,
'instances': 5,
'cores': 21,
'key_pairs': 10,
'floating_ips': 10,
'security_groups': 10,
'security_group_rules': 20,
}
body = self.controller.index(request) body = self.controller.index(request)
expected = { expected = {
"limits": { "limits": {
@@ -168,16 +149,7 @@ class LimitsControllerTest(BaseLimitTestSuite):
], ],
}, },
], ]
"absolute": {
"maxTotalRAMSize": 512,
"maxTotalInstances": 5,
"maxTotalCores": 21,
"maxTotalKeypairs": 10,
"maxTotalFloatingIps": 10,
"maxSecurityGroups": 10,
"maxSecurityGroupRules": 20,
},
}, },
} }
self.assertEqual(expected, body) self.assertEqual(expected, body)
@@ -226,90 +198,11 @@ class LimitsControllerTest(BaseLimitTestSuite):
], ],
}, },
], ]
"absolute": {},
}, },
} }
self.assertEqual(expected, body) self.assertEqual(expected, body)
def _test_index_absolute_limits_json(self, expected):
request = self._get_index_request()
body = self.controller.index(request)
self.assertEqual(expected, body['limits']['absolute'])
def test_index_ignores_extra_absolute_limits_json(self):
self.absolute_limits = {'unknown_limit': 9001}
self._test_index_absolute_limits_json({})
def test_index_absolute_ram_json(self):
self.absolute_limits = {'ram': 1024}
self._test_index_absolute_limits_json({'maxTotalRAMSize': 1024})
def test_index_absolute_cores_json(self):
self.absolute_limits = {'cores': 17}
self._test_index_absolute_limits_json({'maxTotalCores': 17})
def test_index_absolute_instances_json(self):
self.absolute_limits = {'instances': 19}
self._test_index_absolute_limits_json({'maxTotalInstances': 19})
def test_index_absolute_metadata_json(self):
# NOTE: both server metadata and image metadata are overloaded
# into metadata_items
self.absolute_limits = {'metadata_items': 23}
expected = {
'maxServerMeta': 23,
'maxImageMeta': 23,
}
self._test_index_absolute_limits_json(expected)
def test_index_absolute_injected_files(self):
self.absolute_limits = {
'injected_files': 17,
'injected_file_content_bytes': 86753,
}
expected = {
'maxPersonality': 17,
'maxPersonalitySize': 86753,
}
self._test_index_absolute_limits_json(expected)
def test_index_absolute_security_groups(self):
self.absolute_limits = {
'security_groups': 8,
'security_group_rules': 16,
}
expected = {
'maxSecurityGroups': 8,
'maxSecurityGroupRules': 16,
}
self._test_index_absolute_limits_json(expected)
def test_limit_create(self):
req = fakes.HTTPRequestV3.blank('/limits')
self.assertRaises(webob.exc.HTTPNotImplemented, self.controller.create,
req, {})
def test_limit_delete(self):
req = fakes.HTTPRequestV3.blank('/limits')
self.assertRaises(webob.exc.HTTPNotImplemented, self.controller.delete,
req, 1)
def test_limit_detail(self):
req = fakes.HTTPRequestV3.blank('/limits')
self.assertRaises(webob.exc.HTTPNotImplemented, self.controller.detail,
req)
def test_limit_show(self):
req = fakes.HTTPRequestV3.blank('/limits')
self.assertRaises(webob.exc.HTTPNotImplemented, self.controller.show,
req, 1)
def test_limit_update(self):
req = fakes.HTTPRequestV3.blank('/limits')
self.assertRaises(webob.exc.HTTPNotImplemented, self.controller.update,
req, 1, {})
class MockLimiter(limits.Limiter): class MockLimiter(limits.Limiter):
pass pass
@@ -862,7 +755,7 @@ class WsgiLimiterProxyTest(BaseLimitTestSuite):
class LimitsViewBuilderTest(test.NoDBTestCase): class LimitsViewBuilderTest(test.NoDBTestCase):
def setUp(self): def setUp(self):
super(LimitsViewBuilderTest, self).setUp() super(LimitsViewBuilderTest, self).setUp()
self.view_builder = views.limits.ViewBuilder() self.view_builder = views.limits.ViewBuilderV3()
self.rate_limits = [{"URI": "*", self.rate_limits = [{"URI": "*",
"regex": ".*", "regex": ".*",
"value": 10, "value": 10,
@@ -877,9 +770,6 @@ class LimitsViewBuilderTest(test.NoDBTestCase):
"remaining": 10, "remaining": 10,
"unit": "DAY", "unit": "DAY",
"resetTime": 1311272226}] "resetTime": 1311272226}]
self.absolute_limits = {"metadata_items": 1,
"injected_files": 5,
"injected_file_content_bytes": 5}
def test_build_limits(self): def test_build_limits(self):
expected_limits = {"limits": { expected_limits = {"limits": {
@@ -897,23 +787,17 @@ class LimitsViewBuilderTest(test.NoDBTestCase):
"verb": "POST", "verb": "POST",
"remaining": 10, "remaining": 10,
"unit": "DAY", "unit": "DAY",
"next-available": "2011-07-21T18:17:06Z"}]}], "next-available": "2011-07-21T18:17:06Z"}]}]}}
"absolute": {"maxServerMeta": 1,
"maxImageMeta": 1,
"maxPersonality": 5,
"maxPersonalitySize": 5}}}
output = self.view_builder.build(self.rate_limits, output = self.view_builder.build(self.rate_limits)
self.absolute_limits)
self.assertThat(output, matchers.DictMatches(expected_limits)) self.assertThat(output, matchers.DictMatches(expected_limits))
def test_build_limits_empty_limits(self): def test_build_limits_empty_limits(self):
expected_limits = {"limits": {"rate": [], expected_limits = {"limits": {"rate": []}}
"absolute": {}}}
abs_limits = {} abs_limits = {}
rate_limits = [] rate_limits = []
output = self.view_builder.build(rate_limits, abs_limits) output = self.view_builder.build(rate_limits)
self.assertThat(output, matchers.DictMatches(expected_limits)) self.assertThat(output, matchers.DictMatches(expected_limits))
@@ -922,8 +806,7 @@ class LimitsXMLSerializationTest(test.NoDBTestCase):
serializer = limits.LimitsTemplate() serializer = limits.LimitsTemplate()
fixture = {"limits": { fixture = {"limits": {
"rate": [], "rate": []}}
"absolute": {}}}
output = serializer.serialize(fixture) output = serializer.serialize(fixture)
has_dec = output.startswith("<?xml version='1.0' encoding='UTF-8'?>") has_dec = output.startswith("<?xml version='1.0' encoding='UTF-8'?>")
@@ -949,23 +832,11 @@ class LimitsXMLSerializationTest(test.NoDBTestCase):
"verb": "POST", "verb": "POST",
"remaining": 10, "remaining": 10,
"unit": "DAY", "unit": "DAY",
"next-available": "2011-12-15T22:42:45Z"}]}], "next-available": "2011-12-15T22:42:45Z"}]}]}}
"absolute": {"maxServerMeta": 1,
"maxImageMeta": 1,
"maxPersonality": 5,
"maxPersonalitySize": 10240}}}
output = serializer.serialize(fixture) output = serializer.serialize(fixture)
root = etree.XML(output) root = etree.XML(output)
xmlutil.validate_schema(root, 'limits') xmlutil.validate_schema(root, 'limits', version='v3')
#verify absolute limits
absolutes = root.xpath('ns:absolute/ns:limit', namespaces=NS)
self.assertEqual(len(absolutes), 4)
for limit in absolutes:
name = limit.get('name')
value = limit.get('value')
self.assertEqual(value, str(fixture['limits']['absolute'][name]))
#verify rate limits #verify rate limits
rates = root.xpath('ns:rates/ns:rate', namespaces=NS) rates = root.xpath('ns:rates/ns:rate', namespaces=NS)
@@ -985,17 +856,11 @@ class LimitsXMLSerializationTest(test.NoDBTestCase):
def test_index_no_limits(self): def test_index_no_limits(self):
serializer = limits.LimitsTemplate() serializer = limits.LimitsTemplate()
fixture = {"limits": { fixture = {"limits": {"rate": []}}
"rate": [],
"absolute": {}}}
output = serializer.serialize(fixture) output = serializer.serialize(fixture)
root = etree.XML(output) root = etree.XML(output)
xmlutil.validate_schema(root, 'limits') xmlutil.validate_schema(root, 'limits', version='v3')
#verify absolute limits
absolutes = root.xpath('ns:absolute/ns:limit', namespaces=NS)
self.assertEqual(len(absolutes), 0)
#verify rate limits #verify rate limits
rates = root.xpath('ns:rates/ns:rate', namespaces=NS) rates = root.xpath('ns:rates/ns:rate', namespaces=NS)

View File

@@ -1,18 +1,5 @@
{ {
"limits": { "limits": {
"absolute": {
"maxImageMeta": 128,
"maxPersonality": 5,
"maxPersonalitySize": 10240,
"maxSecurityGroupRules": 20,
"maxSecurityGroups": 10,
"maxServerMeta": 128,
"maxTotalCores": 20,
"maxTotalFloatingIps": 10,
"maxTotalInstances": 10,
"maxTotalKeypairs": 100,
"maxTotalRAMSize": 51200
},
"rate": [ "rate": [
{ {
"limit": [ "limit": [

View File

@@ -13,17 +13,4 @@
<limit next-available="%(timestamp)s" unit="MINUTE" verb="GET" remaining="3" value="3"/> <limit next-available="%(timestamp)s" unit="MINUTE" verb="GET" remaining="3" value="3"/>
</rate> </rate>
</rates> </rates>
<absolute>
<limit name="maxServerMeta" value="128"/>
<limit name="maxPersonality" value="5"/>
<limit name="maxImageMeta" value="128"/>
<limit name="maxPersonalitySize" value="10240"/>
<limit name="maxSecurityGroupRules" value="20"/>
<limit name="maxTotalKeypairs" value="100"/>
<limit name="maxSecurityGroups" value="10"/>
<limit name="maxTotalCores" value="20"/>
<limit name="maxTotalFloatingIps" value="10"/>
<limit name="maxTotalInstances" value="10"/>
<limit name="maxTotalRAMSize" value="51200"/>
</absolute>
</limits> </limits>