From 188dd9117318cc4f5ebe0be9d19b9737a43ce68b Mon Sep 17 00:00:00 2001 From: Lorin Hochstein Date: Thu, 23 Jun 2011 23:42:44 -0400 Subject: Starting to transition instance type extra specs API to an extension API --- nova/api/openstack/__init__.py | 5 - nova/api/openstack/contrib/flavorextraspecs.py | 123 +++++++++++++++++++++ nova/api/openstack/flavor_extra_specs.py | 102 ----------------- .../api/openstack/test_flavors_extra_specs.py | 20 +++- 4 files changed, 139 insertions(+), 111 deletions(-) create mode 100644 nova/api/openstack/contrib/flavorextraspecs.py delete mode 100644 nova/api/openstack/flavor_extra_specs.py diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index 857a5431b..859cac669 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -178,8 +178,3 @@ class APIRouterV11(APIRouter): controller=server_metadata.create_resource(), parent_resource=dict(member_name='server', collection_name='servers')) - - mapper.resource("flavor_extra_specs", "extra", - controller=flavor_extra_specs.create_resource(), - parent_resource=dict(member_name='flavor', - collection_name='flavors')) diff --git a/nova/api/openstack/contrib/flavorextraspecs.py b/nova/api/openstack/contrib/flavorextraspecs.py new file mode 100644 index 000000000..24c5da7b2 --- /dev/null +++ b/nova/api/openstack/contrib/flavorextraspecs.py @@ -0,0 +1,123 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 University of Southern California +# 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. + +""" The instance type extra specs extension""" + +from webob import exc + +from nova import db +from nova import quota +from nova.api.openstack import extensions +from nova.api.openstack import faults +from nova.api.openstack import wsgi + + +class FlavorExtraSpecsController(object): + """ The flavor extra specs API controller for the Openstack API """ + + def _get_extra_specs(self, context, flavor_id): + extra_specs = db.api.instance_type_extra_specs_get(context, flavor_id) + specs_dict = {} + for key, value in extra_specs.iteritems(): + specs_dict[key] = value + return dict(extra=specs_dict) + + def _check_body(self, body): + if body == None or body == "": + expl = _('No Request Body') + raise exc.HTTPBadRequest(explanation=expl) + + def index(self, req, flavor_id): + """ Returns the list of extra specs for a givenflavor """ + context = req.environ['nova.context'] + return self._get_extra_specs(context, flavor_id) + + def create(self, req, flavor_id, body): + self._check_body(body) + context = req.environ['nova.context'] + specs = body.get('extra') + try: + db.api.instance_type_extra_specs_update_or_create(context, + flavor_id, + specs) + except quota.QuotaError as error: + self._handle_quota_error(error) + return body + + def update(self, req, flavor_id, id, body): + self._check_body(body) + context = req.environ['nova.context'] + if not id in body: + expl = _('Request body and URI mismatch') + raise exc.HTTPBadRequest(explanation=expl) + if len(body) > 1: + expl = _('Request body contains too many items') + raise exc.HTTPBadRequest(explanation=expl) + try: + db.api.instance_type_extra_specs_update_or_create(context, + flavor_id, + body) + except quota.QuotaError as error: + self._handle_quota_error(error) + + return body + + def show(self, req, flavor_id, id): + """ Return a single extra spec item """ + context = req.environ['nova.context'] + specs = self._get_extra_specs(context, flavor_id) + if id in specs['extra']: + return {id: specs['extra'][id]} + else: + return faults.Fault(exc.HTTPNotFound()) + + def delete(self, req, flavor_id, id): + """ Deletes an existing extra spec """ + context = req.environ['nova.context'] + db.api.instance_type_extra_specs_delete(context, flavor_id, id) + + def _handle_quota_error(self, error): + """Reraise quota errors as api-specific http exceptions.""" + if error.code == "MetadataLimitExceeded": + raise exc.HTTPBadRequest(explanation=error.message) + raise error + +class Flavorextraspecs(extensions.ExtensionDescriptor): + def get_name(self): + return "FlavorExtraSpecs" + + def get_alias(self): + return "flavor-extra-specs" + + def get_description(self): + return "Instance type (flavor) extra specs" + + def get_namespace(self): + return \ + "http://docs.openstack.org/ext/flavor-extra-specs/api/v1.1" + + def get_updated(self): + return "2011-06-23T00:00:00+00:00" + + def get_resources(self): + resources = [] + + res = extensions.ResourceExtension('flavor-extra-specs', + FlavorExtraSpecsController()) + resources.append(res) + + return resources diff --git a/nova/api/openstack/flavor_extra_specs.py b/nova/api/openstack/flavor_extra_specs.py deleted file mode 100644 index 6a6d2f7a1..000000000 --- a/nova/api/openstack/flavor_extra_specs.py +++ /dev/null @@ -1,102 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 University of Southern California -# 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 webob import exc - -from nova import db -from nova import quota -from nova.api.openstack import faults -from nova.api.openstack import wsgi - - -class Controller(object): - """ The flavor extra specs API controller for the Openstack API """ - - def _get_extra_specs(self, context, flavor_id): - extra_specs = db.api.instance_type_extra_specs_get(context, flavor_id) - specs_dict = {} - for key, value in extra_specs.iteritems(): - specs_dict[key] = value - return dict(extra=specs_dict) - - def _check_body(self, body): - if body == None or body == "": - expl = _('No Request Body') - raise exc.HTTPBadRequest(explanation=expl) - - def index(self, req, flavor_id): - """ Returns the list of extra specs for a givenflavor """ - context = req.environ['nova.context'] - return self._get_extra_specs(context, flavor_id) - - def create(self, req, flavor_id, body): - self._check_body(body) - context = req.environ['nova.context'] - specs = body.get('extra') - try: - db.api.instance_type_extra_specs_update_or_create(context, - flavor_id, - specs) - except quota.QuotaError as error: - self._handle_quota_error(error) - return body - - def update(self, req, flavor_id, id, body): - self._check_body(body) - context = req.environ['nova.context'] - if not id in body: - expl = _('Request body and URI mismatch') - raise exc.HTTPBadRequest(explanation=expl) - if len(body) > 1: - expl = _('Request body contains too many items') - raise exc.HTTPBadRequest(explanation=expl) - try: - db.api.instance_type_extra_specs_update_or_create(context, - flavor_id, - body) - except quota.QuotaError as error: - self._handle_quota_error(error) - - return body - - def show(self, req, flavor_id, id): - """ Return a single extra spec item """ - context = req.environ['nova.context'] - specs = self._get_extra_specs(context, flavor_id) - if id in specs['extra']: - return {id: specs['extra'][id]} - else: - return faults.Fault(exc.HTTPNotFound()) - - def delete(self, req, flavor_id, id): - """ Deletes an existing extra spec """ - context = req.environ['nova.context'] - db.api.instance_type_extra_specs_delete(context, flavor_id, id) - - def _handle_quota_error(self, error): - """Reraise quota errors as api-specific http exceptions.""" - if error.code == "MetadataLimitExceeded": - raise exc.HTTPBadRequest(explanation=error.message) - raise error - - -def create_resource(): - serializers = { - 'application/xml': wsgi.XMLDictSerializer(xmlns=wsgi.XMLNS_V11), - } - - return wsgi.Resource(Controller(), serializers=serializers) diff --git a/nova/tests/api/openstack/test_flavors_extra_specs.py b/nova/tests/api/openstack/test_flavors_extra_specs.py index 14f6e7d43..1fe0884b6 100644 --- a/nova/tests/api/openstack/test_flavors_extra_specs.py +++ b/nova/tests/api/openstack/test_flavors_extra_specs.py @@ -19,12 +19,16 @@ import json import stubout import unittest import webob +import os.path + from nova import flags from nova.api import openstack +from nova.api.openstack import extensions from nova.tests.api.openstack import fakes import nova.wsgi +FLAGS = flags.FLAGS def return_create_flavor_extra_specs(context, flavor_id, extra_specs): return stub_flavor_extra_specs() @@ -60,6 +64,8 @@ class FlavorsExtraSpecsTest(unittest.TestCase): def setUp(self): super(FlavorsExtraSpecsTest, self).setUp() + FLAGS.osapi_extensions_path = os.path.join(os.path.dirname(__file__), + "extensions") self.stubs = stubout.StubOutForTesting() fakes.FakeAuthManager.auth_data = {} fakes.FakeAuthDatabase.data = {} @@ -73,13 +79,19 @@ class FlavorsExtraSpecsTest(unittest.TestCase): def test_index(self): self.stubs.Set(nova.db.api, 'instance_type_extra_specs_get', return_flavor_extra_specs) - req = webob.Request.blank('/v1.1/flavors/1/extra') - req.environ['api.version'] = '1.1' - res = req.get_response(fakes.wsgi_app()) + app = openstack.APIRouterV11() + ext_midware = extensions.ExtensionMiddleware(app) + #request = webob.Request.blank('/flavors-extra-specs/1') + request = webob.Request.blank('/flavors-extra-specs') + res = request.get_response(ext_midware) + print res + #req.environ['api.version'] = '1.1' + #res = req.get_response(fakes.wsgi_app()) self.assertEqual(200, res.status_int) res_dict = json.loads(res.body) self.assertEqual('application/json', res.headers['Content-Type']) - self.assertEqual('value1', res_dict['extra']['key1']) + print res_dict + self.assertEqual('value1', res_dict['1']['key1']) def test_index_no_data(self): self.stubs.Set(nova.db.api, 'instance_type_extra_specs_get', -- cgit