summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Yeoh <cyeoh@au1.ibm.com>2013-06-14 14:53:24 +0930
committerChris Yeoh <cyeoh@au1.ibm.com>2013-06-24 11:07:21 +0930
commite7a8dc0ce6e812ebd3874be2f18cf33e46095edb (patch)
tree0c0b264f9ad8e8c01fdf1a56667ca1d0b4d8c128
parentcefb0510b8f12dab17126907661d82094c31741d (diff)
downloadnova-e7a8dc0ce6e812ebd3874be2f18cf33e46095edb.tar.gz
nova-e7a8dc0ce6e812ebd3874be2f18cf33e46095edb.tar.xz
nova-e7a8dc0ce6e812ebd3874be2f18cf33e46095edb.zip
Port quota classes extension to v3 API Part 1
This changeset only copies the v2 files (implementation and test) into the appropriate v3 directories unchanged. The copy as-is will not be loaded by either the v2 or v3 extension loaders. The second changeset will then make the changes required for it to work as a v3 extension. This is being done in order to make reviewing of extension porting easier as gerrit will display only what is actually changed for v3 rather than entirely new files Partially implements blueprint nova-v3-api Change-Id: I328b32aee29021bae42cba5d97e0735e7b691ac9
-rw-r--r--nova/api/openstack/compute/plugins/v3/quota_classes.py103
-rw-r--r--nova/tests/api/openstack/compute/plugins/v3/test_quota_classes.py188
2 files changed, 291 insertions, 0 deletions
diff --git a/nova/api/openstack/compute/plugins/v3/quota_classes.py b/nova/api/openstack/compute/plugins/v3/quota_classes.py
new file mode 100644
index 000000000..361748df8
--- /dev/null
+++ b/nova/api/openstack/compute/plugins/v3/quota_classes.py
@@ -0,0 +1,103 @@
+# Copyright 2012 OpenStack Foundation
+# 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 webob
+
+from nova.api.openstack import extensions
+from nova.api.openstack import wsgi
+from nova.api.openstack import xmlutil
+import nova.context
+from nova import db
+from nova import exception
+from nova import quota
+
+
+QUOTAS = quota.QUOTAS
+
+
+authorize = extensions.extension_authorizer('compute', 'quota_classes')
+
+
+class QuotaClassTemplate(xmlutil.TemplateBuilder):
+ def construct(self):
+ root = xmlutil.TemplateElement('quota_class_set',
+ selector='quota_class_set')
+ root.set('id')
+
+ for resource in QUOTAS.resources:
+ elem = xmlutil.SubTemplateElement(root, resource)
+ elem.text = resource
+
+ return xmlutil.MasterTemplate(root, 1)
+
+
+class QuotaClassSetsController(object):
+
+ def _format_quota_set(self, quota_class, quota_set):
+ """Convert the quota object to a result dict."""
+
+ result = dict(id=str(quota_class))
+
+ for resource in QUOTAS.resources:
+ result[resource] = quota_set[resource]
+
+ return dict(quota_class_set=result)
+
+ @wsgi.serializers(xml=QuotaClassTemplate)
+ def show(self, req, id):
+ context = req.environ['nova.context']
+ authorize(context)
+ try:
+ nova.context.authorize_quota_class_context(context, id)
+ return self._format_quota_set(id,
+ QUOTAS.get_class_quotas(context, id))
+ except exception.NotAuthorized:
+ raise webob.exc.HTTPForbidden()
+
+ @wsgi.serializers(xml=QuotaClassTemplate)
+ def update(self, req, id, body):
+ context = req.environ['nova.context']
+ authorize(context)
+ quota_class = id
+ for key in body['quota_class_set'].keys():
+ if key in QUOTAS:
+ value = int(body['quota_class_set'][key])
+ try:
+ db.quota_class_update(context, quota_class, key, value)
+ except exception.QuotaClassNotFound:
+ db.quota_class_create(context, quota_class, key, value)
+ except exception.AdminRequired:
+ raise webob.exc.HTTPForbidden()
+ return {'quota_class_set': QUOTAS.get_class_quotas(context,
+ quota_class)}
+
+
+class Quota_classes(extensions.ExtensionDescriptor):
+ """Quota classes management support."""
+
+ name = "QuotaClasses"
+ alias = "os-quota-class-sets"
+ namespace = ("http://docs.openstack.org/compute/ext/"
+ "quota-classes-sets/api/v1.1")
+ updated = "2012-03-12T00:00:00+00:00"
+
+ def get_resources(self):
+ resources = []
+
+ res = extensions.ResourceExtension('os-quota-class-sets',
+ QuotaClassSetsController())
+ resources.append(res)
+
+ return resources
diff --git a/nova/tests/api/openstack/compute/plugins/v3/test_quota_classes.py b/nova/tests/api/openstack/compute/plugins/v3/test_quota_classes.py
new file mode 100644
index 000000000..c4f79118e
--- /dev/null
+++ b/nova/tests/api/openstack/compute/plugins/v3/test_quota_classes.py
@@ -0,0 +1,188 @@
+# Copyright 2012 OpenStack Foundation
+# 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.
+
+from lxml import etree
+import webob
+
+from nova.api.openstack.compute.contrib import quota_classes
+from nova.api.openstack import wsgi
+from nova import test
+from nova.tests.api.openstack import fakes
+
+
+def quota_set(class_name):
+ return {'quota_class_set': {'id': class_name, 'metadata_items': 128,
+ 'ram': 51200, 'floating_ips': 10,
+ 'fixed_ips': -1, 'instances': 10,
+ 'injected_files': 5, 'cores': 20,
+ 'injected_file_content_bytes': 10240,
+ 'security_groups': 10,
+ 'security_group_rules': 20, 'key_pairs': 100,
+ 'injected_file_path_bytes': 255}}
+
+
+class QuotaClassSetsTest(test.TestCase):
+
+ def setUp(self):
+ super(QuotaClassSetsTest, self).setUp()
+ self.controller = quota_classes.QuotaClassSetsController()
+
+ def test_format_quota_set(self):
+ raw_quota_set = {
+ 'instances': 10,
+ 'cores': 20,
+ 'ram': 51200,
+ 'floating_ips': 10,
+ 'fixed_ips': -1,
+ 'metadata_items': 128,
+ 'injected_files': 5,
+ 'injected_file_path_bytes': 255,
+ 'injected_file_content_bytes': 10240,
+ 'security_groups': 10,
+ 'security_group_rules': 20,
+ 'key_pairs': 100,
+ }
+
+ quota_set = self.controller._format_quota_set('test_class',
+ raw_quota_set)
+ qs = quota_set['quota_class_set']
+
+ self.assertEqual(qs['id'], 'test_class')
+ self.assertEqual(qs['instances'], 10)
+ self.assertEqual(qs['cores'], 20)
+ self.assertEqual(qs['ram'], 51200)
+ self.assertEqual(qs['floating_ips'], 10)
+ self.assertEqual(qs['fixed_ips'], -1)
+ self.assertEqual(qs['metadata_items'], 128)
+ self.assertEqual(qs['injected_files'], 5)
+ self.assertEqual(qs['injected_file_path_bytes'], 255)
+ self.assertEqual(qs['injected_file_content_bytes'], 10240)
+ self.assertEqual(qs['security_groups'], 10)
+ self.assertEqual(qs['security_group_rules'], 20)
+ self.assertEqual(qs['key_pairs'], 100)
+
+ def test_quotas_show_as_admin(self):
+ req = fakes.HTTPRequest.blank(
+ '/v2/fake4/os-quota-class-sets/test_class',
+ use_admin_context=True)
+ res_dict = self.controller.show(req, 'test_class')
+
+ self.assertEqual(res_dict, quota_set('test_class'))
+
+ def test_quotas_show_as_unauthorized_user(self):
+ req = fakes.HTTPRequest.blank(
+ '/v2/fake4/os-quota-class-sets/test_class')
+ self.assertRaises(webob.exc.HTTPForbidden, self.controller.show,
+ req, 'test_class')
+
+ def test_quotas_update_as_admin(self):
+ body = {'quota_class_set': {'instances': 50, 'cores': 50,
+ 'ram': 51200, 'floating_ips': 10,
+ 'fixed_ips': -1, 'metadata_items': 128,
+ 'injected_files': 5,
+ 'injected_file_content_bytes': 10240,
+ 'injected_file_path_bytes': 255,
+ 'security_groups': 10,
+ 'security_group_rules': 20,
+ 'key_pairs': 100}}
+
+ req = fakes.HTTPRequest.blank(
+ '/v2/fake4/os-quota-class-sets/test_class',
+ use_admin_context=True)
+ res_dict = self.controller.update(req, 'test_class', body)
+
+ self.assertEqual(res_dict, body)
+
+ def test_quotas_update_as_user(self):
+ body = {'quota_class_set': {'instances': 50, 'cores': 50,
+ 'ram': 51200, 'floating_ips': 10,
+ 'fixed_ips': -1, 'metadata_items': 128,
+ 'injected_files': 5,
+ 'injected_file_content_bytes': 10240,
+ 'security_groups': 10,
+ 'security_group_rules': 20,
+ 'key_pairs': 100,
+ }}
+
+ req = fakes.HTTPRequest.blank(
+ '/v2/fake4/os-quota-class-sets/test_class')
+ self.assertRaises(webob.exc.HTTPForbidden, self.controller.update,
+ req, 'test_class', body)
+
+
+class QuotaTemplateXMLSerializerTest(test.TestCase):
+ def setUp(self):
+ super(QuotaTemplateXMLSerializerTest, self).setUp()
+ self.serializer = quota_classes.QuotaClassTemplate()
+ self.deserializer = wsgi.XMLDeserializer()
+
+ def test_serializer(self):
+ exemplar = dict(quota_class_set=dict(
+ id='test_class',
+ metadata_items=10,
+ injected_file_path_bytes=255,
+ injected_file_content_bytes=20,
+ ram=50,
+ floating_ips=60,
+ fixed_ips=-1,
+ instances=70,
+ injected_files=80,
+ security_groups=10,
+ security_group_rules=20,
+ key_pairs=100,
+ cores=90))
+ text = self.serializer.serialize(exemplar)
+
+ tree = etree.fromstring(text)
+
+ self.assertEqual('quota_class_set', tree.tag)
+ self.assertEqual('test_class', tree.get('id'))
+ self.assertEqual(len(exemplar['quota_class_set']) - 1, len(tree))
+ for child in tree:
+ self.assertTrue(child.tag in exemplar['quota_class_set'])
+ self.assertEqual(int(child.text),
+ exemplar['quota_class_set'][child.tag])
+
+ def test_deserializer(self):
+ exemplar = dict(quota_class_set=dict(
+ metadata_items='10',
+ injected_file_content_bytes='20',
+ ram='50',
+ floating_ips='60',
+ fixed_ips='-1',
+ instances='70',
+ injected_files='80',
+ security_groups='10',
+ security_group_rules='20',
+ key_pairs='100',
+ cores='90'))
+ intext = ("<?xml version='1.0' encoding='UTF-8'?>\n"
+ '<quota_class_set>'
+ '<metadata_items>10</metadata_items>'
+ '<injected_file_content_bytes>20'
+ '</injected_file_content_bytes>'
+ '<ram>50</ram>'
+ '<floating_ips>60</floating_ips>'
+ '<fixed_ips>-1</fixed_ips>'
+ '<instances>70</instances>'
+ '<injected_files>80</injected_files>'
+ '<cores>90</cores>'
+ '<security_groups>10</security_groups>'
+ '<security_group_rules>20</security_group_rules>'
+ '<key_pairs>100</key_pairs>'
+ '</quota_class_set>')
+
+ result = self.deserializer.deserialize(intext)['body']
+ self.assertEqual(result, exemplar)