From 0c1484ec0366bcb60210281472ea61f3d2abad7b Mon Sep 17 00:00:00 2001 From: Dan Smith Date: Tue, 17 Jun 2014 17:59:04 -0700 Subject: [PATCH] Object-ify APIv2 flavorextraspecs extension This makes the flavorextraspecs extension use the Flavor object. Related to blueprint compute-manager-objects-juno Change-Id: I28bcc24382f1e993b98b3f7342fed53c9ae85cbf --- .../compute/contrib/flavorextraspecs.py | 47 ++++++++++++------- .../contrib/test_flavors_extra_specs.py | 43 +++++++++++++---- 2 files changed, 66 insertions(+), 24 deletions(-) diff --git a/nova/api/openstack/compute/contrib/flavorextraspecs.py b/nova/api/openstack/compute/contrib/flavorextraspecs.py index 58a5dc0f24d5..441cb29d0aa2 100644 --- a/nova/api/openstack/compute/contrib/flavorextraspecs.py +++ b/nova/api/openstack/compute/contrib/flavorextraspecs.py @@ -21,8 +21,8 @@ from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova.api.openstack import xmlutil from nova.compute import flavors -from nova import db from nova import exception +from nova import objects from nova.openstack.common.gettextutils import _ from nova import utils @@ -48,8 +48,8 @@ class FlavorExtraSpecsController(object): """The flavor extra specs API controller for the OpenStack API.""" def _get_extra_specs(self, context, flavor_id): - extra_specs = db.flavor_extra_specs_get(context, flavor_id) - return dict(extra_specs=extra_specs) + flavor = objects.Flavor.get_by_flavor_id(context, flavor_id) + return dict(extra_specs=flavor.extra_specs) def _check_body(self, body): if body is None or body == "": @@ -90,11 +90,13 @@ class FlavorExtraSpecsController(object): specs = body.get('extra_specs') self._check_extra_specs(specs) try: - db.flavor_extra_specs_update_or_create(context, - flavor_id, - specs) + flavor = objects.Flavor.get_by_flavor_id(context, flavor_id) + flavor.extra_specs = dict(flavor.extra_specs, **specs) + flavor.save() except exception.MetadataLimitExceeded as error: raise exc.HTTPBadRequest(explanation=error.format_message()) + except exception.FlavorNotFound as error: + raise exc.HTTPNotFound(explanation=error.format_message()) return body @wsgi.serializers(xml=ExtraSpecTemplate) @@ -109,11 +111,13 @@ class FlavorExtraSpecsController(object): expl = _('Request body contains too many items') raise exc.HTTPBadRequest(explanation=expl) try: - db.flavor_extra_specs_update_or_create(context, - flavor_id, - body) + flavor = objects.Flavor.get_by_flavor_id(context, flavor_id) + flavor.extra_specs = dict(flavor.extra_specs, **body) + flavor.save() except exception.MetadataLimitExceeded as error: raise exc.HTTPBadRequest(explanation=error.format_message()) + except exception.FlavorNotFound as error: + raise exc.HTTPNotFound(explanation=error.format_message()) return body @wsgi.serializers(xml=ExtraSpecTemplate) @@ -122,20 +126,31 @@ class FlavorExtraSpecsController(object): context = req.environ['nova.context'] authorize(context, action='show') try: - extra_spec = db.flavor_extra_specs_get_item(context, - flavor_id, id) - return extra_spec - except exception.FlavorExtraSpecsNotFound: - raise exc.HTTPNotFound() + flavor = objects.Flavor.get_by_flavor_id(context, flavor_id) + return {id: flavor.extra_specs[id]} + except exception.FlavorNotFound as error: + raise exc.HTTPNotFound(explanation=error.format_message()) + except KeyError: + msg = _("Flavor %(flavor_id)s has no extra specs with " + "key %(key)s.") % dict(flavor_id=flavor_id, + key=id) + raise exc.HTTPNotFound(explanation=msg) def delete(self, req, flavor_id, id): """Deletes an existing extra spec.""" context = req.environ['nova.context'] authorize(context, action='delete') try: - db.flavor_extra_specs_delete(context, flavor_id, id) - except exception.FlavorExtraSpecsNotFound as e: + flavor = objects.Flavor.get_by_flavor_id(context, flavor_id) + del flavor.extra_specs[id] + flavor.save() + except exception.FlavorNotFound as e: raise exc.HTTPNotFound(explanation=e.format_message()) + except KeyError: + msg = _("Flavor %(flavor_id)s has no extra specs with " + "key %(key)s.") % dict(flavor_id=flavor_id, + key=id) + raise exc.HTTPNotFound(explanation=msg) class Flavorextraspecs(extensions.ExtensionDescriptor): diff --git a/nova/tests/api/openstack/compute/contrib/test_flavors_extra_specs.py b/nova/tests/api/openstack/compute/contrib/test_flavors_extra_specs.py index 3658733b7ad4..3f416e123538 100644 --- a/nova/tests/api/openstack/compute/contrib/test_flavors_extra_specs.py +++ b/nova/tests/api/openstack/compute/contrib/test_flavors_extra_specs.py @@ -21,6 +21,7 @@ import nova.db from nova import exception from nova import test from nova.tests.api.openstack import fakes +from nova.tests.objects import test_flavor def return_create_flavor_extra_specs(context, flavor_id, extra_specs): @@ -61,11 +62,13 @@ class FlavorsExtraSpecsTest(test.TestCase): self.controller = flavorextraspecs.FlavorExtraSpecsController() def test_index(self): - self.stubs.Set(nova.db, 'flavor_extra_specs_get', - return_flavor_extra_specs) + flavor = dict(test_flavor.fake_flavor, + extra_specs={'key1': 'value1'}) req = fakes.HTTPRequest.blank('/v2/fake/flavors/1/os-extra_specs') - res_dict = self.controller.index(req, 1) + with mock.patch('nova.db.flavor_get_by_flavor_id') as mock_get: + mock_get.return_value = flavor + res_dict = self.controller.index(req, 1) self.assertEqual('value1', res_dict['extra_specs']['key1']) @@ -79,12 +82,13 @@ class FlavorsExtraSpecsTest(test.TestCase): self.assertEqual(0, len(res_dict['extra_specs'])) def test_show(self): - self.stubs.Set(nova.db, 'flavor_extra_specs_get_item', - return_flavor_extra_specs_item) - + flavor = dict(test_flavor.fake_flavor, + extra_specs={'key5': 'value5'}) req = fakes.HTTPRequest.blank('/v2/fake/flavors/1/os-extra_specs' + '/key5') - res_dict = self.controller.show(req, 1, 'key5') + with mock.patch('nova.db.flavor_get_by_flavor_id') as mock_get: + mock_get.return_value = flavor + res_dict = self.controller.show(req, 1, 'key5') self.assertEqual('value5', res_dict['key5']) @@ -97,13 +101,36 @@ class FlavorsExtraSpecsTest(test.TestCase): self.assertRaises(webob.exc.HTTPNotFound, self.controller.show, req, 1, 'key6') + def test_not_found_because_flavor(self): + req = fakes.HTTPRequestV3.blank('/flavors/1/extra-specs/key5', + use_admin_context=True) + with mock.patch('nova.db.flavor_get_by_flavor_id') as mock_get: + mock_get.side_effect = exception.FlavorNotFound(flavor_id='1') + self.assertRaises(webob.exc.HTTPNotFound, self.controller.show, + req, 1, 'key5') + self.assertRaises(webob.exc.HTTPNotFound, self.controller.update, + req, 1, 'key5', {'key5': 'value5'}) + self.assertRaises(webob.exc.HTTPNotFound, self.controller.delete, + req, 1, 'key5') + + req = fakes.HTTPRequestV3.blank('/flavors/1/extra-specs', + use_admin_context=True) + with mock.patch('nova.db.flavor_get_by_flavor_id') as mock_get: + mock_get.side_effect = exception.FlavorNotFound(flavor_id='1') + self.assertRaises(webob.exc.HTTPNotFound, self.controller.create, + req, 1, {'extra_specs': {'key5': 'value5'}}) + def test_delete(self): + flavor = dict(test_flavor.fake_flavor, + extra_specs={'key5': 'value5'}) self.stubs.Set(nova.db, 'flavor_extra_specs_delete', delete_flavor_extra_specs) req = fakes.HTTPRequest.blank('/v2/fake/flavors/1/os-extra_specs' + '/key5', use_admin_context=True) - self.controller.delete(req, 1, 'key5') + with mock.patch('nova.db.flavor_get_by_flavor_id') as mock_get: + mock_get.return_value = flavor + self.controller.delete(req, 1, 'key5') def test_delete_no_admin(self): self.stubs.Set(nova.db, 'flavor_extra_specs_delete',