summaryrefslogtreecommitdiff
path: root/nova
diff options
context:
space:
mode:
authorKen'ichi Ohmichi <oomichi@mxs.nes.nec.co.jp>2014-08-11 12:22:09 +0000
committerKen'ichi Ohmichi <oomichi@mxs.nes.nec.co.jp>2014-08-27 05:52:32 +0000
commite6a642f07632a6233538ca5762cb1cca6e136e9f (patch)
tree579fa9c259bcc64bae1d50e8ca8b40e77c6960cf /nova
parent3ab81c9b3eb87093b902c25e4edfc9eaf986356c (diff)
downloadnova-e6a642f07632a6233538ca5762cb1cca6e136e9f.tar.gz
Add v2.1 API router and endpoint
Now we have decided Nova v3 API interfaces are disabled and we will change v3 interfaces to v2 ones for using v3 infrastractures as v2.1 API. The big difference between v2 and v3 that v2 API URLs contain project-id but v3 ones don't contain it. This patch adds both an API router and an endpoint of v2.1 API to support API URLs containing project-id. This patch does not remove v3 API router and endpoint because v3 API tests use it now. After moving all v3 code to v2.1, we can remove them. Partially implements blueprint v2-on-v3-api Change-Id: Iee75fbd8951c9f246f68a9c75d762335fa5c4b4b
Diffstat (limited to 'nova')
-rw-r--r--nova/api/auth.py6
-rw-r--r--nova/api/openstack/__init__.py18
-rw-r--r--nova/api/openstack/compute/__init__.py22
-rw-r--r--nova/tests/api/openstack/compute/test_v21_extensions.py196
-rw-r--r--nova/tests/api/test_auth.py4
5 files changed, 236 insertions, 10 deletions
diff --git a/nova/api/auth.py b/nova/api/auth.py
index 710281a00d..43b4dbef88 100644
--- a/nova/api/auth.py
+++ b/nova/api/auth.py
@@ -75,11 +75,15 @@ def pipeline_factory(loader, global_conf, **local_conf):
return _load_pipeline(loader, pipeline)
-def pipeline_factory_v3(loader, global_conf, **local_conf):
+def pipeline_factory_v21(loader, global_conf, **local_conf):
"""A paste pipeline replica that keys off of auth_strategy."""
return _load_pipeline(loader, local_conf[CONF.auth_strategy].split())
+# NOTE(oomichi): This pipeline_factory_v3 is for passing check-grenade-dsvm.
+pipeline_factory_v3 = pipeline_factory_v21
+
+
class InjectContext(wsgi.Middleware):
"""Add a 'nova.context' to WSGI environ."""
diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py
index b0e1e9e884..e737704f6c 100644
--- a/nova/api/openstack/__init__.py
+++ b/nova/api/openstack/__init__.py
@@ -249,11 +249,13 @@ class APIRouter(base_wsgi.Router):
raise NotImplementedError()
-class APIRouterV3(base_wsgi.Router):
- """Routes requests on the OpenStack v3 API to the appropriate controller
+class APIRouterV21(base_wsgi.Router):
+ """Routes requests on the OpenStack v2.1 API to the appropriate controller
and method.
"""
+ # TODO(oomichi): This namespace will be changed after moving all v3 APIs
+ # to v2.1.
API_EXTENSION_NAMESPACE = 'nova.api.v3.extensions'
@classmethod
@@ -261,10 +263,12 @@ class APIRouterV3(base_wsgi.Router):
"""Simple paste factory, :class:`nova.wsgi.Router` doesn't have one."""
return cls()
- def __init__(self, init_only=None):
+ def __init__(self, init_only=None, v3mode=False):
# TODO(cyeoh): bp v3-api-extension-framework. Currently load
# all extensions but eventually should be able to exclude
# based on a config file
+ # TODO(oomichi): We can remove v3mode argument after moving all v3 APIs
+ # to v2.1.
def _check_load_extension(ext):
if (self.init_only is None or ext.obj.alias in
self.init_only) and isinstance(ext.obj,
@@ -313,7 +317,11 @@ class APIRouterV3(base_wsgi.Router):
invoke_on_load=True,
invoke_kwds={"extension_info": self.loaded_extension_info})
- mapper = PlainMapper()
+ if v3mode:
+ mapper = PlainMapper()
+ else:
+ mapper = ProjectMapper()
+
self.resources = {}
# NOTE(cyeoh) Core API support is rewritten as extensions
@@ -333,7 +341,7 @@ class APIRouterV3(base_wsgi.Router):
raise exception.CoreAPIMissing(
missing_apis=missing_core_extensions)
- super(APIRouterV3, self).__init__(mapper)
+ super(APIRouterV21, self).__init__(mapper)
@staticmethod
def get_missing_core_extensions(extensions_loaded):
diff --git a/nova/api/openstack/compute/__init__.py b/nova/api/openstack/compute/__init__.py
index bca184cedb..300e7c8fb4 100644
--- a/nova/api/openstack/compute/__init__.py
+++ b/nova/api/openstack/compute/__init__.py
@@ -128,13 +128,31 @@ class APIRouter(nova.api.openstack.APIRouter):
conditions={"method": ['PUT']})
-class APIRouterV3(nova.api.openstack.APIRouterV3):
+class APIRouterV21(nova.api.openstack.APIRouterV21):
"""Routes requests on the OpenStack API to the appropriate controller
and method.
"""
def __init__(self, init_only=None):
self._loaded_extension_info = plugins.LoadedExtensionInfo()
- super(APIRouterV3, self).__init__(init_only)
+ super(APIRouterV21, self).__init__(init_only)
+
+ def _register_extension(self, ext):
+ return self.loaded_extension_info.register_extension(ext.obj)
+
+ @property
+ def loaded_extension_info(self):
+ return self._loaded_extension_info
+
+
+# NOTE(oomichi): Now v3 API tests use APIRouterV3. After moving all v3
+# API extensions to v2.1 API, we can remove this class.
+class APIRouterV3(nova.api.openstack.APIRouterV21):
+ """Routes requests on the OpenStack API to the appropriate controller
+ and method.
+ """
+ def __init__(self, init_only=None):
+ self._loaded_extension_info = plugins.LoadedExtensionInfo()
+ super(APIRouterV3, self).__init__(init_only, v3mode=True)
def _register_extension(self, ext):
return self.loaded_extension_info.register_extension(ext.obj)
diff --git a/nova/tests/api/openstack/compute/test_v21_extensions.py b/nova/tests/api/openstack/compute/test_v21_extensions.py
new file mode 100644
index 0000000000..7998dc82e5
--- /dev/null
+++ b/nova/tests/api/openstack/compute/test_v21_extensions.py
@@ -0,0 +1,196 @@
+# Copyright 2013 IBM Corp.
+# Copyright 2014 NEC Corporation. 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 oslo.config import cfg
+import stevedore
+import webob.exc
+
+from nova.api import openstack
+from nova.api.openstack import compute
+from nova.api.openstack.compute import plugins
+from nova.api.openstack import extensions
+from nova import exception
+from nova import test
+
+CONF = cfg.CONF
+
+
+class fake_bad_extension(object):
+ name = "fake_bad_extension"
+ alias = "fake-bad"
+
+
+class fake_stevedore_enabled_extensions(object):
+ def __init__(self, namespace, check_func, invoke_on_load=False,
+ invoke_args=(), invoke_kwds=None):
+ self.extensions = []
+
+ def map(self, func, *args, **kwds):
+ pass
+
+ def __iter__(self):
+ return iter(self.extensions)
+
+
+class fake_loaded_extension_info(object):
+ def __init__(self):
+ self.extensions = {}
+
+ def register_extension(self, ext):
+ self.extensions[ext] = ext
+ return True
+
+ def get_extensions(self):
+ return {'core1': None, 'core2': None, 'noncore1': None}
+
+
+class ExtensionLoadingTestCase(test.NoDBTestCase):
+
+ def _set_v21_core(self, core_extensions):
+ openstack.API_V3_CORE_EXTENSIONS = core_extensions
+
+ def test_extensions_loaded(self):
+ app = compute.APIRouterV21()
+ self.assertIn('servers', app._loaded_extension_info.extensions)
+
+ def test_check_bad_extension(self):
+ extension_info = plugins.LoadedExtensionInfo()
+ self.assertFalse(extension_info._check_extension(fake_bad_extension))
+
+ def test_extensions_blacklist(self):
+ app = compute.APIRouterV21()
+ self.assertIn('os-hosts', app._loaded_extension_info.extensions)
+ CONF.set_override('extensions_blacklist', ['os-hosts'], 'osapi_v3')
+ app = compute.APIRouterV21()
+ self.assertNotIn('os-hosts', app._loaded_extension_info.extensions)
+
+ def test_extensions_whitelist_accept(self):
+ # NOTE(maurosr): just to avoid to get an exception raised for not
+ # loading all core api.
+ v21_core = openstack.API_V3_CORE_EXTENSIONS
+ openstack.API_V3_CORE_EXTENSIONS = set(['servers'])
+ self.addCleanup(self._set_v21_core, v21_core)
+
+ app = compute.APIRouterV21()
+ self.assertIn('os-hosts', app._loaded_extension_info.extensions)
+ CONF.set_override('extensions_whitelist', ['servers', 'os-hosts'],
+ 'osapi_v3')
+ app = compute.APIRouterV21()
+ self.assertIn('os-hosts', app._loaded_extension_info.extensions)
+
+ def test_extensions_whitelist_block(self):
+ # NOTE(maurosr): just to avoid to get an exception raised for not
+ # loading all core api.
+ v21_core = openstack.API_V3_CORE_EXTENSIONS
+ openstack.API_V3_CORE_EXTENSIONS = set(['servers'])
+ self.addCleanup(self._set_v21_core, v21_core)
+
+ app = compute.APIRouterV21()
+ self.assertIn('os-hosts', app._loaded_extension_info.extensions)
+ CONF.set_override('extensions_whitelist', ['servers'], 'osapi_v3')
+ app = compute.APIRouterV21()
+ self.assertNotIn('os-hosts', app._loaded_extension_info.extensions)
+
+ def test_blacklist_overrides_whitelist(self):
+ # NOTE(maurosr): just to avoid to get an exception raised for not
+ # loading all core api.
+ v21_core = openstack.API_V3_CORE_EXTENSIONS
+ openstack.API_V3_CORE_EXTENSIONS = set(['servers'])
+ self.addCleanup(self._set_v21_core, v21_core)
+
+ app = compute.APIRouterV21()
+ self.assertIn('os-hosts', app._loaded_extension_info.extensions)
+ CONF.set_override('extensions_whitelist', ['servers', 'os-hosts'],
+ 'osapi_v3')
+ CONF.set_override('extensions_blacklist', ['os-hosts'], 'osapi_v3')
+ app = compute.APIRouterV21()
+ self.assertNotIn('os-hosts', app._loaded_extension_info.extensions)
+ self.assertIn('servers', app._loaded_extension_info.extensions)
+ self.assertEqual(1, len(app._loaded_extension_info.extensions))
+
+ def test_get_missing_core_extensions(self):
+ v21_core = openstack.API_V3_CORE_EXTENSIONS
+ openstack.API_V3_CORE_EXTENSIONS = set(['core1', 'core2'])
+ self.addCleanup(self._set_v21_core, v21_core)
+ self.assertEqual(0, len(
+ compute.APIRouterV21.get_missing_core_extensions(
+ ['core1', 'core2', 'noncore1'])))
+ missing_core = compute.APIRouterV21.get_missing_core_extensions(
+ ['core1'])
+ self.assertEqual(1, len(missing_core))
+ self.assertIn('core2', missing_core)
+ missing_core = compute.APIRouterV21.get_missing_core_extensions([])
+ self.assertEqual(2, len(missing_core))
+ self.assertIn('core1', missing_core)
+ self.assertIn('core2', missing_core)
+ missing_core = compute.APIRouterV21.get_missing_core_extensions(
+ ['noncore1'])
+ self.assertEqual(2, len(missing_core))
+ self.assertIn('core1', missing_core)
+ self.assertIn('core2', missing_core)
+
+ def test_core_extensions_present(self):
+ self.stubs.Set(stevedore.enabled, 'EnabledExtensionManager',
+ fake_stevedore_enabled_extensions)
+ self.stubs.Set(plugins, 'LoadedExtensionInfo',
+ fake_loaded_extension_info)
+ v21_core = openstack.API_V3_CORE_EXTENSIONS
+ openstack.API_V3_CORE_EXTENSIONS = set(['core1', 'core2'])
+ self.addCleanup(self._set_v21_core, v21_core)
+ # if no core API extensions are missing then an exception will
+ # not be raised when creating an instance of compute.APIRouterV21
+ compute.APIRouterV21()
+
+ def test_core_extensions_missing(self):
+ self.stubs.Set(stevedore.enabled, 'EnabledExtensionManager',
+ fake_stevedore_enabled_extensions)
+ self.stubs.Set(plugins, 'LoadedExtensionInfo',
+ fake_loaded_extension_info)
+ self.assertRaises(exception.CoreAPIMissing, compute.APIRouterV21)
+
+ def test_extensions_expected_error(self):
+ @extensions.expected_errors(404)
+ def fake_func():
+ raise webob.exc.HTTPNotFound()
+
+ self.assertRaises(webob.exc.HTTPNotFound, fake_func)
+
+ def test_extensions_expected_error_from_list(self):
+ @extensions.expected_errors((404, 403))
+ def fake_func():
+ raise webob.exc.HTTPNotFound()
+
+ self.assertRaises(webob.exc.HTTPNotFound, fake_func)
+
+ def test_extensions_unexpected_error(self):
+ @extensions.expected_errors(404)
+ def fake_func():
+ raise webob.exc.HTTPConflict()
+
+ self.assertRaises(webob.exc.HTTPInternalServerError, fake_func)
+
+ def test_extensions_unexpected_error_from_list(self):
+ @extensions.expected_errors((404, 413))
+ def fake_func():
+ raise webob.exc.HTTPConflict()
+
+ self.assertRaises(webob.exc.HTTPInternalServerError, fake_func)
+
+ def test_extensions_unexpected_policy_not_authorized_error(self):
+ @extensions.expected_errors(404)
+ def fake_func():
+ raise exception.PolicyNotAuthorized(action="foo")
+
+ self.assertRaises(exception.PolicyNotAuthorized, fake_func)
diff --git a/nova/tests/api/test_auth.py b/nova/tests/api/test_auth.py
index 8505e381e6..a7bb247489 100644
--- a/nova/tests/api/test_auth.py
+++ b/nova/tests/api/test_auth.py
@@ -173,9 +173,9 @@ class TestPipeLineFactory(test.NoDBTestCase):
TestPipeLineFactory.FakeLoader(), None, noauth=fake_pipeline)
self._test_pipeline(fake_pipeline, app)
- def test_pipeline_factory_v3(self):
+ def test_pipeline_factory_v21(self):
fake_pipeline = 'test1 test2 test3'
- app = nova.api.auth.pipeline_factory_v3(
+ app = nova.api.auth.pipeline_factory_v21(
TestPipeLineFactory.FakeLoader(), None, noauth=fake_pipeline)
self._test_pipeline(fake_pipeline, app)