diff --git a/nova/api/openstack/placement/handler.py b/nova/api/openstack/placement/handler.py index 5d5377f4c..f769c58fa 100644 --- a/nova/api/openstack/placement/handler.py +++ b/nova/api/openstack/placement/handler.py @@ -48,6 +48,14 @@ ROUTE_DECLARATIONS = { '/': { 'GET': root.home, }, + # NOTE(cdent): This allows '/placement/' and '/placement' to + # both work as the root of the service, which we probably want + # for those situations where the service is mounted under a + # prefix (as it is in devstack). While weird, an empty string is + # a legit key in a dictionary and matches as desired in Routes. + '': { + 'GET': root.home, + }, '/resource_providers': { 'GET': resource_provider.list_resource_providers, 'POST': resource_provider.create_resource_provider diff --git a/nova/tests/unit/api/openstack/placement/test_handler.py b/nova/tests/unit/api/openstack/placement/test_handler.py index b0a81e15e..e59e80fed 100644 --- a/nova/tests/unit/api/openstack/placement/test_handler.py +++ b/nova/tests/unit/api/openstack/placement/test_handler.py @@ -18,6 +18,7 @@ import routes import webob from nova.api.openstack.placement import handler +from nova.api.openstack.placement.handlers import root from nova import test from nova.tests import uuidsentinel @@ -112,3 +113,20 @@ class PlacementLoggingTest(test.NoDBTestCase): app, environ, start_response) mocked_log.error.assert_not_called() mocked_log.exception.assert_not_called() + + +class DeclarationsTest(test.NoDBTestCase): + + def setUp(self): + super(DeclarationsTest, self).setUp() + self.mapper = handler.make_map(handler.ROUTE_DECLARATIONS) + + def test_root_slash_match(self): + environ = _environ(path='/') + result = self.mapper.match(environ=environ) + self.assertEqual(root.home, result['action']) + + def test_root_empty_match(self): + environ = _environ(path='') + result = self.mapper.match(environ=environ) + self.assertEqual(root.home, result['action'])