diff --git a/neutron/manager.py b/neutron/manager.py index 65b31ac3c89..fcbf78ac2ca 100644 --- a/neutron/manager.py +++ b/neutron/manager.py @@ -254,3 +254,11 @@ class NeutronManager(object): @classmethod def get_controller_for_resource(cls, resource): return cls.get_instance().resource_controller_mappings.get(resource) + + @classmethod + def get_service_plugin_by_path_prefix(cls, path_prefix): + service_plugins = cls.get_unique_service_plugins() + for service_plugin in service_plugins: + plugin_path_prefix = getattr(service_plugin, 'path_prefix', None) + if plugin_path_prefix and plugin_path_prefix == path_prefix: + return service_plugin diff --git a/neutron/pecan_wsgi/controllers/root.py b/neutron/pecan_wsgi/controllers/root.py index 15b509d5e1a..e796112febd 100644 --- a/neutron/pecan_wsgi/controllers/root.py +++ b/neutron/pecan_wsgi/controllers/root.py @@ -104,6 +104,15 @@ class V2Controller(object): @expose() def _lookup(self, collection, *remainder): + # if collection exists in the extension to service plugins map then + # we are assuming that collection is the service plugin and + # needs to be remapped. + # Example: https://neutron.endpoint/v2.0/lbaas/loadbalancers + if (remainder and + manager.NeutronManager.get_service_plugin_by_path_prefix( + collection)): + collection = remainder[0] + remainder = remainder[1:] controller = manager.NeutronManager.get_controller_for_resource( collection) if not controller: diff --git a/neutron/tests/functional/pecan_wsgi/test_functional.py b/neutron/tests/functional/pecan_wsgi/test_functional.py index 96c4207494b..69e68c62391 100644 --- a/neutron/tests/functional/pecan_wsgi/test_functional.py +++ b/neutron/tests/functional/pecan_wsgi/test_functional.py @@ -15,10 +15,12 @@ import os +from collections import namedtuple import mock from oslo_config import cfg from oslo_serialization import jsonutils from oslo_utils import uuidutils +import pecan from pecan import request from pecan import set_config from pecan.testing import load_test_app @@ -32,6 +34,20 @@ from neutron import manager from neutron.pecan_wsgi.controllers import root as controllers from neutron.tests.unit import testlib_api +_SERVICE_PLUGIN_RESOURCE = 'serviceplugin' +_SERVICE_PLUGIN_COLLECTION = _SERVICE_PLUGIN_RESOURCE + 's' +_SERVICE_PLUGIN_INDEX_BODY = {_SERVICE_PLUGIN_COLLECTION: []} + + +class FakeServicePluginController(object): + resource = _SERVICE_PLUGIN_RESOURCE + + @pecan.expose(generic=True, + content_type='application/json', + template='json') + def index(self): + return _SERVICE_PLUGIN_INDEX_BODY + class PecanFunctionalTest(testlib_api.SqlTestCase): @@ -42,6 +58,7 @@ class PecanFunctionalTest(testlib_api.SqlTestCase): self.addCleanup(set_config, {}, overwrite=True) self.set_config_overrides() self.setup_app() + self.setup_service_plugin() def setup_app(self): self.app = load_test_app(os.path.join( @@ -67,6 +84,10 @@ class PecanFunctionalTest(testlib_api.SqlTestCase): def set_config_overrides(self): cfg.CONF.set_override('auth_strategy', 'noauth') + def setup_service_plugin(self): + manager.NeutronManager.set_controller_for_resource( + _SERVICE_PLUGIN_COLLECTION, FakeServicePluginController()) + class TestV2Controller(PecanFunctionalTest): @@ -104,6 +125,15 @@ class TestV2Controller(PecanFunctionalTest): response = self.app.get('/v2.0/extensions/allowed-address-pairs.json') self.assertEqual(response.status_int, 200) + def test_service_plugin_uri(self): + service_plugin = namedtuple('DummyServicePlugin', 'path_prefix') + service_plugin.path_prefix = 'dummy' + nm = manager.NeutronManager.get_instance() + nm.service_plugins['dummy_sp'] = service_plugin + response = self.app.get('/v2.0/dummy/serviceplugins.json') + self.assertEqual(200, response.status_int) + self.assertEqual(_SERVICE_PLUGIN_INDEX_BODY, response.json_body) + class TestErrors(PecanFunctionalTest): diff --git a/neutron/tests/unit/test_manager.py b/neutron/tests/unit/test_manager.py index 80b0a31a03a..c90d0405132 100644 --- a/neutron/tests/unit/test_manager.py +++ b/neutron/tests/unit/test_manager.py @@ -13,6 +13,8 @@ # License for the specific language governing permissions and limitations # under the License. +import weakref + import fixtures from oslo_config import cfg @@ -147,3 +149,20 @@ class NeutronManagerTestCase(base.BaseTestCase): with testlib_api.ExpectedException(ImportError): manager.NeutronManager.load_class_for_provider( 'neutron.core_plugins', 'ml2XXXXXX') + + def test_get_service_plugin_by_path_prefix_3(self): + cfg.CONF.set_override("core_plugin", DB_PLUGIN_KLASS) + nm = manager.NeutronManager.get_instance() + + class pclass(object): + def __init__(self, path_prefix): + self.path_prefix = path_prefix + + x_plugin, y_plugin = pclass('xpa'), pclass('ypa') + nm.service_plugins['x'], nm.service_plugins['y'] = x_plugin, y_plugin + + self.assertEqual(weakref.proxy(x_plugin), + nm.get_service_plugin_by_path_prefix('xpa')) + self.assertEqual(weakref.proxy(y_plugin), + nm.get_service_plugin_by_path_prefix('ypa')) + self.assertIsNone(nm.get_service_plugin_by_path_prefix('abc'))