Merge "Make project_id optional in v2.1 urls"
This commit is contained in:
		@@ -19,7 +19,7 @@
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
        "status": "CURRENT",
 | 
					        "status": "CURRENT",
 | 
				
			||||||
        "version": "2.17",
 | 
					        "version": "2.18",
 | 
				
			||||||
        "min_version": "2.1",
 | 
					        "min_version": "2.1",
 | 
				
			||||||
        "updated": "2013-07-23T11:33:21Z"
 | 
					        "updated": "2013-07-23T11:33:21Z"
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,7 +22,7 @@
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
            "status": "CURRENT",
 | 
					            "status": "CURRENT",
 | 
				
			||||||
            "version": "2.17",
 | 
					            "version": "2.18",
 | 
				
			||||||
            "min_version": "2.1",
 | 
					            "min_version": "2.1",
 | 
				
			||||||
            "updated": "2013-07-23T11:33:21Z"
 | 
					            "updated": "2013-07-23T11:33:21Z"
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -60,7 +60,13 @@ api_opts = [
 | 
				
			|||||||
                    'list. Specify the extension aliases here. '
 | 
					                    'list. Specify the extension aliases here. '
 | 
				
			||||||
                    'This option will be removed in the near future. '
 | 
					                    'This option will be removed in the near future. '
 | 
				
			||||||
                    'After that point you have to run all of the API.',
 | 
					                    'After that point you have to run all of the API.',
 | 
				
			||||||
                    deprecated_for_removal=True, deprecated_group='osapi_v21')
 | 
					                    deprecated_for_removal=True, deprecated_group='osapi_v21'),
 | 
				
			||||||
 | 
					        cfg.StrOpt('project_id_regex',
 | 
				
			||||||
 | 
					                   default=None,
 | 
				
			||||||
 | 
					                   help='DEPRECATED: The validation regex for project_ids '
 | 
				
			||||||
 | 
					                   'used in urls. This defaults to [0-9a-f\-]+ if not set, '
 | 
				
			||||||
 | 
					                   'which matches normal uuids created by keystone.',
 | 
				
			||||||
 | 
					                   deprecated_for_removal=True, deprecated_group='osapi_v21')
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
api_opts_group = cfg.OptGroup(name='osapi_v21', title='API v2.1 Options')
 | 
					api_opts_group = cfg.OptGroup(name='osapi_v21', title='API v2.1 Options')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -196,14 +202,40 @@ class APIMapper(routes.Mapper):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class ProjectMapper(APIMapper):
 | 
					class ProjectMapper(APIMapper):
 | 
				
			||||||
    def resource(self, member_name, collection_name, **kwargs):
 | 
					    def resource(self, member_name, collection_name, **kwargs):
 | 
				
			||||||
 | 
					        # NOTE(sdague): project_id parameter is only valid if its hex
 | 
				
			||||||
 | 
					        # or hex + dashes (note, integers are a subset of this). This
 | 
				
			||||||
 | 
					        # is required to hand our overlaping routes issues.
 | 
				
			||||||
 | 
					        project_id_regex = '[0-9a-f\-]+'
 | 
				
			||||||
 | 
					        if CONF.osapi_v21.project_id_regex:
 | 
				
			||||||
 | 
					            project_id_regex = CONF.osapi_v21.project_id_regex
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        project_id_token = '{project_id:%s}' % project_id_regex
 | 
				
			||||||
        if 'parent_resource' not in kwargs:
 | 
					        if 'parent_resource' not in kwargs:
 | 
				
			||||||
            kwargs['path_prefix'] = '{project_id}/'
 | 
					            kwargs['path_prefix'] = '%s/' % project_id_token
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            parent_resource = kwargs['parent_resource']
 | 
					            parent_resource = kwargs['parent_resource']
 | 
				
			||||||
            p_collection = parent_resource['collection_name']
 | 
					            p_collection = parent_resource['collection_name']
 | 
				
			||||||
            p_member = parent_resource['member_name']
 | 
					            p_member = parent_resource['member_name']
 | 
				
			||||||
            kwargs['path_prefix'] = '{project_id}/%s/:%s_id' % (p_collection,
 | 
					            kwargs['path_prefix'] = '%s/%s/:%s_id' % (
 | 
				
			||||||
                                                                p_member)
 | 
					                project_id_token,
 | 
				
			||||||
 | 
					                p_collection,
 | 
				
			||||||
 | 
					                p_member)
 | 
				
			||||||
 | 
					        routes.Mapper.resource(
 | 
				
			||||||
 | 
					            self,
 | 
				
			||||||
 | 
					            member_name,
 | 
				
			||||||
 | 
					            collection_name,
 | 
				
			||||||
 | 
					            **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # while we are in transition mode, create additional routes
 | 
				
			||||||
 | 
					        # for the resource that do not include project_id.
 | 
				
			||||||
 | 
					        if 'parent_resource' not in kwargs:
 | 
				
			||||||
 | 
					            del kwargs['path_prefix']
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            parent_resource = kwargs['parent_resource']
 | 
				
			||||||
 | 
					            p_collection = parent_resource['collection_name']
 | 
				
			||||||
 | 
					            p_member = parent_resource['member_name']
 | 
				
			||||||
 | 
					            kwargs['path_prefix'] = '%s/:%s_id' % (p_collection,
 | 
				
			||||||
 | 
					                                                   p_member)
 | 
				
			||||||
        routes.Mapper.resource(self, member_name,
 | 
					        routes.Mapper.resource(self, member_name,
 | 
				
			||||||
                                     collection_name,
 | 
					                                     collection_name,
 | 
				
			||||||
                                     **kwargs)
 | 
					                                     **kwargs)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -59,6 +59,7 @@ REST_API_VERSION_HISTORY = """REST API Version History:
 | 
				
			|||||||
    * 2.15 - Add soft-affinity and soft-anti-affinity policies
 | 
					    * 2.15 - Add soft-affinity and soft-anti-affinity policies
 | 
				
			||||||
    * 2.16 - Exposes host_status for servers/detail and servers/{server_id}
 | 
					    * 2.16 - Exposes host_status for servers/detail and servers/{server_id}
 | 
				
			||||||
    * 2.17 - Add trigger_crash_dump to server actions
 | 
					    * 2.17 - Add trigger_crash_dump to server actions
 | 
				
			||||||
 | 
					    * 2.18 - Makes project_id optional in v2.1
 | 
				
			||||||
"""
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# The minimum and maximum versions of the API supported
 | 
					# The minimum and maximum versions of the API supported
 | 
				
			||||||
@@ -67,7 +68,7 @@ REST_API_VERSION_HISTORY = """REST API Version History:
 | 
				
			|||||||
# Note(cyeoh): This only applies for the v2.1 API once microversions
 | 
					# Note(cyeoh): This only applies for the v2.1 API once microversions
 | 
				
			||||||
# support is fully merged. It does not affect the V2 API.
 | 
					# support is fully merged. It does not affect the V2 API.
 | 
				
			||||||
_MIN_API_VERSION = "2.1"
 | 
					_MIN_API_VERSION = "2.1"
 | 
				
			||||||
_MAX_API_VERSION = "2.17"
 | 
					_MAX_API_VERSION = "2.18"
 | 
				
			||||||
DEFAULT_API_VERSION = _MIN_API_VERSION
 | 
					DEFAULT_API_VERSION = _MIN_API_VERSION
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -74,10 +74,14 @@ class NoAuthMiddleware(NoAuthMiddlewareBase):
 | 
				
			|||||||
        return self.base_call(req, True, always_admin=False)
 | 
					        return self.base_call(req, True, always_admin=False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# TODO(johnthetubaguy) this should be removed in the M release
 | 
					class NoAuthMiddlewareV2_17(NoAuthMiddlewareBase):
 | 
				
			||||||
class NoAuthMiddlewareV3(NoAuthMiddlewareBase):
 | 
					    """Return a fake token if one isn't specified.
 | 
				
			||||||
    """Return a fake token if one isn't specified."""
 | 
					
 | 
				
			||||||
 | 
					    This provides a version of the middleware which does not add
 | 
				
			||||||
 | 
					    project_id into server management urls.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @webob.dec.wsgify(RequestClass=wsgi.Request)
 | 
					    @webob.dec.wsgify(RequestClass=wsgi.Request)
 | 
				
			||||||
    def __call__(self, req):
 | 
					    def __call__(self, req):
 | 
				
			||||||
        return self.base_call(req, False)
 | 
					        return self.base_call(req, False, always_admin=False)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -154,3 +154,8 @@ class ImageMetadata(extensions.V21APIExtensionBase):
 | 
				
			|||||||
                       "/{project_id}/images/{image_id}/metadata",
 | 
					                       "/{project_id}/images/{image_id}/metadata",
 | 
				
			||||||
                       controller=wsgi_resource,
 | 
					                       controller=wsgi_resource,
 | 
				
			||||||
                       action='update_all', conditions={"method": ['PUT']})
 | 
					                       action='update_all', conditions={"method": ['PUT']})
 | 
				
			||||||
 | 
					        # Also connect the non project_id route
 | 
				
			||||||
 | 
					        mapper.connect("metadata",
 | 
				
			||||||
 | 
					                       "/images/{image_id}/metadata",
 | 
				
			||||||
 | 
					                       controller=wsgi_resource,
 | 
				
			||||||
 | 
					                       action='update_all', conditions={"method": ['PUT']})
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -191,3 +191,8 @@ class ServerMetadata(extensions.V21APIExtensionBase):
 | 
				
			|||||||
                       "/{project_id}/servers/{server_id}/metadata",
 | 
					                       "/{project_id}/servers/{server_id}/metadata",
 | 
				
			||||||
                       controller=wsgi_resource,
 | 
					                       controller=wsgi_resource,
 | 
				
			||||||
                       action='update_all', conditions={"method": ['PUT']})
 | 
					                       action='update_all', conditions={"method": ['PUT']})
 | 
				
			||||||
 | 
					        # Also connect the non project_id routes
 | 
				
			||||||
 | 
					        mapper.connect("metadata",
 | 
				
			||||||
 | 
					                       "/servers/{server_id}/metadata",
 | 
				
			||||||
 | 
					                       controller=wsgi_resource,
 | 
				
			||||||
 | 
					                       action='update_all', conditions={"method": ['PUT']})
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -163,3 +163,7 @@ user documentation.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  Add a new API for triggering crash dump in an instance. Different operation
 | 
					  Add a new API for triggering crash dump in an instance. Different operation
 | 
				
			||||||
  systems in instance may need different configurations to trigger crash dump.
 | 
					  systems in instance may need different configurations to trigger crash dump.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2.18
 | 
				
			||||||
 | 
					----
 | 
				
			||||||
 | 
					  Establishes a set of routes that makes project_id an optional construct in v2.1.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -54,3 +54,14 @@ class ApiPasteLegacyV2Fixture(ApiPasteV21Fixture):
 | 
				
			|||||||
            "/v2: openstack_compute_api_v21_legacy_v2_compatible",
 | 
					            "/v2: openstack_compute_api_v21_legacy_v2_compatible",
 | 
				
			||||||
            "/v2: openstack_compute_api_legacy_v2")
 | 
					            "/v2: openstack_compute_api_legacy_v2")
 | 
				
			||||||
        target_file.write(line)
 | 
					        target_file.write(line)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ApiPasteNoProjectId(ApiPasteV21Fixture):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _replace_line(self, target_file, line):
 | 
				
			||||||
 | 
					        line = line.replace(
 | 
				
			||||||
 | 
					            "paste.filter_factory = nova.api.openstack.auth:"
 | 
				
			||||||
 | 
					            "NoAuthMiddleware.factory",
 | 
				
			||||||
 | 
					            "paste.filter_factory = nova.api.openstack.auth:"
 | 
				
			||||||
 | 
					            "NoAuthMiddlewareV2_17.factory")
 | 
				
			||||||
 | 
					        target_file.write(line)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -69,6 +69,7 @@ class ApiSampleTestBaseV21(testscenarios.WithScenarios,
 | 
				
			|||||||
    sample_dir = None
 | 
					    sample_dir = None
 | 
				
			||||||
    extra_extensions_to_load = None
 | 
					    extra_extensions_to_load = None
 | 
				
			||||||
    _legacy_v2_code = False
 | 
					    _legacy_v2_code = False
 | 
				
			||||||
 | 
					    _project_id = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    scenarios = [
 | 
					    scenarios = [
 | 
				
			||||||
        # test v2 with the v2.1 compatibility stack
 | 
					        # test v2 with the v2.1 compatibility stack
 | 
				
			||||||
@@ -82,7 +83,13 @@ class ApiSampleTestBaseV21(testscenarios.WithScenarios,
 | 
				
			|||||||
            'api_major_version': 'v2',
 | 
					            'api_major_version': 'v2',
 | 
				
			||||||
            '_legacy_v2_code': True,
 | 
					            '_legacy_v2_code': True,
 | 
				
			||||||
            '_additional_fixtures': [
 | 
					            '_additional_fixtures': [
 | 
				
			||||||
                api_paste_fixture.ApiPasteLegacyV2Fixture]})
 | 
					                api_paste_fixture.ApiPasteLegacyV2Fixture]}),
 | 
				
			||||||
 | 
					        # test v2.16 code without project id
 | 
				
			||||||
 | 
					        ('v2_1_noproject_id', {
 | 
				
			||||||
 | 
					            'api_major_version': 'v2.1',
 | 
				
			||||||
 | 
					            '_project_id': False,
 | 
				
			||||||
 | 
					            '_additional_fixtures': [
 | 
				
			||||||
 | 
					                api_paste_fixture.ApiPasteNoProjectId]})
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def setUp(self):
 | 
					    def setUp(self):
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,7 +19,7 @@
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
        "status": "CURRENT",
 | 
					        "status": "CURRENT",
 | 
				
			||||||
        "version": "2.17",
 | 
					        "version": "2.18",
 | 
				
			||||||
        "min_version": "2.1",
 | 
					        "min_version": "2.1",
 | 
				
			||||||
        "updated": "2013-07-23T11:33:21Z"
 | 
					        "updated": "2013-07-23T11:33:21Z"
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,7 +22,7 @@
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
            "status": "CURRENT",
 | 
					            "status": "CURRENT",
 | 
				
			||||||
            "version": "2.17",
 | 
					            "version": "2.18",
 | 
				
			||||||
            "min_version": "2.1",
 | 
					            "min_version": "2.1",
 | 
				
			||||||
            "updated": "2013-07-23T11:33:21Z"
 | 
					            "updated": "2013-07-23T11:33:21Z"
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -250,9 +250,19 @@ class ApiSampleTestBase(integrated_helpers._IntegratedTestBase):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def _update_links(self, sample_data):
 | 
					    def _update_links(self, sample_data):
 | 
				
			||||||
        """Process sample data and update version specific links."""
 | 
					        """Process sample data and update version specific links."""
 | 
				
			||||||
        url_re = self._get_host() + "/v(2\.1|2)"
 | 
					        # replace version urls
 | 
				
			||||||
 | 
					        url_re = self._get_host() + "/v(2|2\.1)/openstack"
 | 
				
			||||||
        new_url = self._get_host() + "/" + self.api_major_version
 | 
					        new_url = self._get_host() + "/" + self.api_major_version
 | 
				
			||||||
 | 
					        if self._project_id:
 | 
				
			||||||
 | 
					            new_url += "/openstack"
 | 
				
			||||||
        updated_data = re.sub(url_re, new_url, sample_data)
 | 
					        updated_data = re.sub(url_re, new_url, sample_data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # replace unversioned urls
 | 
				
			||||||
 | 
					        url_re = self._get_host() + "/openstack"
 | 
				
			||||||
 | 
					        new_url = self._get_host()
 | 
				
			||||||
 | 
					        if self._project_id:
 | 
				
			||||||
 | 
					            new_url += "/openstack"
 | 
				
			||||||
 | 
					        updated_data = re.sub(url_re, new_url, updated_data)
 | 
				
			||||||
        return updated_data
 | 
					        return updated_data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _verify_response(self, name, subs, response, exp_code,
 | 
					    def _verify_response(self, name, subs, response, exp_code,
 | 
				
			||||||
@@ -360,13 +370,19 @@ class ApiSampleTestBase(integrated_helpers._IntegratedTestBase):
 | 
				
			|||||||
    def _get_compute_endpoint(self):
 | 
					    def _get_compute_endpoint(self):
 | 
				
			||||||
        # NOTE(sdague): "openstack" is stand in for project_id, it
 | 
					        # NOTE(sdague): "openstack" is stand in for project_id, it
 | 
				
			||||||
        # should be more generic in future.
 | 
					        # should be more generic in future.
 | 
				
			||||||
        return '%s/%s' % (self._get_host(), 'openstack')
 | 
					        if self._project_id:
 | 
				
			||||||
 | 
					            return '%s/%s' % (self._get_host(), 'openstack')
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            return self._get_host()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _get_vers_compute_endpoint(self):
 | 
					    def _get_vers_compute_endpoint(self):
 | 
				
			||||||
        # NOTE(sdague): "openstack" is stand in for project_id, it
 | 
					        # NOTE(sdague): "openstack" is stand in for project_id, it
 | 
				
			||||||
        # should be more generic in future.
 | 
					        # should be more generic in future.
 | 
				
			||||||
        return '%s/%s/%s' % (self._get_host(), self.api_major_version,
 | 
					        if self._project_id:
 | 
				
			||||||
                             'openstack')
 | 
					            return '%s/%s/%s' % (self._get_host(), self.api_major_version,
 | 
				
			||||||
 | 
					                                 'openstack')
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            return '%s/%s' % (self._get_host(), self.api_major_version)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _get_response(self, url, method, body=None, strip_version=False,
 | 
					    def _get_response(self, url, method, body=None, strip_version=False,
 | 
				
			||||||
                      api_version=None, headers=None):
 | 
					                      api_version=None, headers=None):
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,6 +17,8 @@
 | 
				
			|||||||
import webob
 | 
					import webob
 | 
				
			||||||
import webob.dec
 | 
					import webob.dec
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import testscenarios
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from nova.api import openstack as openstack_api
 | 
					from nova.api import openstack as openstack_api
 | 
				
			||||||
from nova.api.openstack import auth
 | 
					from nova.api.openstack import auth
 | 
				
			||||||
from nova.api.openstack import compute
 | 
					from nova.api.openstack import compute
 | 
				
			||||||
@@ -25,15 +27,29 @@ from nova import test
 | 
				
			|||||||
from nova.tests.unit.api.openstack import fakes
 | 
					from nova.tests.unit.api.openstack import fakes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TestNoAuthMiddlewareV21(test.NoDBTestCase):
 | 
					class TestNoAuthMiddleware(testscenarios.WithScenarios, test.NoDBTestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    scenarios = [
 | 
				
			||||||
 | 
					        ('project_id', {
 | 
				
			||||||
 | 
					            'expected_url': 'http://localhost/v2.1/user1_project',
 | 
				
			||||||
 | 
					            'auth_middleware': auth.NoAuthMiddleware}),
 | 
				
			||||||
 | 
					        ('no_project_id', {
 | 
				
			||||||
 | 
					            'expected_url': 'http://localhost/v2.1',
 | 
				
			||||||
 | 
					            'auth_middleware': auth.NoAuthMiddlewareV2_17}),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def setUp(self):
 | 
					    def setUp(self):
 | 
				
			||||||
        super(TestNoAuthMiddlewareV21, self).setUp()
 | 
					        super(TestNoAuthMiddleware, self).setUp()
 | 
				
			||||||
        fakes.stub_out_rate_limiting(self.stubs)
 | 
					        fakes.stub_out_rate_limiting(self.stubs)
 | 
				
			||||||
        fakes.stub_out_networking(self)
 | 
					        fakes.stub_out_networking(self)
 | 
				
			||||||
        self.wsgi_app = fakes.wsgi_app_v21(use_no_auth=True)
 | 
					        api_v21 = openstack_api.FaultWrapper(
 | 
				
			||||||
        self.req_url = '/v2'
 | 
					            self.auth_middleware(
 | 
				
			||||||
        self.expected_url = "http://localhost/v2/user1_project"
 | 
					                compute.APIRouterV21()
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.wsgi_app = urlmap.URLMap()
 | 
				
			||||||
 | 
					        self.wsgi_app['/v2.1'] = api_v21
 | 
				
			||||||
 | 
					        self.req_url = '/v2.1'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_authorize_user(self):
 | 
					    def test_authorize_user(self):
 | 
				
			||||||
        req = webob.Request.blank(self.req_url)
 | 
					        req = webob.Request.blank(self.req_url)
 | 
				
			||||||
@@ -66,16 +82,3 @@ class TestNoAuthMiddlewareV21(test.NoDBTestCase):
 | 
				
			|||||||
        self.assertEqual(result.status, '204 No Content')
 | 
					        self.assertEqual(result.status, '204 No Content')
 | 
				
			||||||
        self.assertNotIn('X-CDN-Management-Url', result.headers)
 | 
					        self.assertNotIn('X-CDN-Management-Url', result.headers)
 | 
				
			||||||
        self.assertNotIn('X-Storage-Url', result.headers)
 | 
					        self.assertNotIn('X-Storage-Url', result.headers)
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class TestNoAuthMiddlewareV3(TestNoAuthMiddlewareV21):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def setUp(self):
 | 
					 | 
				
			||||||
        super(TestNoAuthMiddlewareV3, self).setUp()
 | 
					 | 
				
			||||||
        api_router = compute.APIRouterV3()
 | 
					 | 
				
			||||||
        api_v3 = openstack_api.FaultWrapper(auth.NoAuthMiddlewareV3(
 | 
					 | 
				
			||||||
            api_router))
 | 
					 | 
				
			||||||
        self.wsgi_app = urlmap.URLMap()
 | 
					 | 
				
			||||||
        self.wsgi_app['/v3'] = api_v3
 | 
					 | 
				
			||||||
        self.req_url = '/v3'
 | 
					 | 
				
			||||||
        self.expected_url = "http://localhost/v3"
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -66,7 +66,7 @@ EXP_VERSIONS = {
 | 
				
			|||||||
    "v2.1": {
 | 
					    "v2.1": {
 | 
				
			||||||
        "id": "v2.1",
 | 
					        "id": "v2.1",
 | 
				
			||||||
        "status": "CURRENT",
 | 
					        "status": "CURRENT",
 | 
				
			||||||
        "version": "2.17",
 | 
					        "version": "2.18",
 | 
				
			||||||
        "min_version": "2.1",
 | 
					        "min_version": "2.1",
 | 
				
			||||||
        "updated": "2013-07-23T11:33:21Z",
 | 
					        "updated": "2013-07-23T11:33:21Z",
 | 
				
			||||||
        "links": [
 | 
					        "links": [
 | 
				
			||||||
@@ -128,7 +128,7 @@ class VersionsTestV20(test.NoDBTestCase):
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                "id": "v2.1",
 | 
					                "id": "v2.1",
 | 
				
			||||||
                "status": "CURRENT",
 | 
					                "status": "CURRENT",
 | 
				
			||||||
                "version": "2.17",
 | 
					                "version": "2.18",
 | 
				
			||||||
                "min_version": "2.1",
 | 
					                "min_version": "2.1",
 | 
				
			||||||
                "updated": "2013-07-23T11:33:21Z",
 | 
					                "updated": "2013-07-23T11:33:21Z",
 | 
				
			||||||
                "links": [
 | 
					                "links": [
 | 
				
			||||||
@@ -194,7 +194,7 @@ class VersionsTestV20(test.NoDBTestCase):
 | 
				
			|||||||
        self._test_get_version_2_detail('/', accept=accept)
 | 
					        self._test_get_version_2_detail('/', accept=accept)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_get_version_2_versions_invalid(self):
 | 
					    def test_get_version_2_versions_invalid(self):
 | 
				
			||||||
        req = webob.Request.blank('/v2/versions/1234')
 | 
					        req = webob.Request.blank('/v2/versions/1234/foo')
 | 
				
			||||||
        req.accept = "application/json"
 | 
					        req.accept = "application/json"
 | 
				
			||||||
        res = req.get_response(self.wsgi_app)
 | 
					        res = req.get_response(self.wsgi_app)
 | 
				
			||||||
        self.assertEqual(404, res.status_int)
 | 
					        self.assertEqual(404, res.status_int)
 | 
				
			||||||
@@ -483,7 +483,7 @@ class VersionsTestV21(test.NoDBTestCase):
 | 
				
			|||||||
        self.assertEqual(expected, version)
 | 
					        self.assertEqual(expected, version)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_get_version_21_versions_invalid(self):
 | 
					    def test_get_version_21_versions_invalid(self):
 | 
				
			||||||
        req = webob.Request.blank('/v2.1/versions/1234')
 | 
					        req = webob.Request.blank('/v2.1/versions/1234/foo')
 | 
				
			||||||
        req.accept = "application/json"
 | 
					        req.accept = "application/json"
 | 
				
			||||||
        res = req.get_response(fakes.wsgi_app_v21())
 | 
					        res = req.get_response(fakes.wsgi_app_v21())
 | 
				
			||||||
        self.assertEqual(404, res.status_int)
 | 
					        self.assertEqual(404, res.status_int)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -63,6 +63,11 @@ class ConfFixture(config_fixture.Config):
 | 
				
			|||||||
                group='api_database')
 | 
					                group='api_database')
 | 
				
			||||||
        self.conf.set_default('fatal_exception_format_errors', True)
 | 
					        self.conf.set_default('fatal_exception_format_errors', True)
 | 
				
			||||||
        self.conf.set_default('enabled', True, 'osapi_v21')
 | 
					        self.conf.set_default('enabled', True, 'osapi_v21')
 | 
				
			||||||
 | 
					        # TODO(sdague): this makes our project_id match 'fake' and
 | 
				
			||||||
 | 
					        # 'openstack' as well. We should fix the tests to use real
 | 
				
			||||||
 | 
					        # UUIDs then drop this work around.
 | 
				
			||||||
 | 
					        self.conf.set_default('project_id_regex',
 | 
				
			||||||
 | 
					                              '[0-9a-fopnstk\-]+', 'osapi_v21')
 | 
				
			||||||
        self.conf.set_default('force_dhcp_release', False)
 | 
					        self.conf.set_default('force_dhcp_release', False)
 | 
				
			||||||
        self.conf.set_default('periodic_enable', False)
 | 
					        self.conf.set_default('periodic_enable', False)
 | 
				
			||||||
        policy_opts.set_defaults(self.conf)
 | 
					        policy_opts.set_defaults(self.conf)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										16
									
								
								releasenotes/notes/optional_project_id-6aebf1cb394d498f.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								releasenotes/notes/optional_project_id-6aebf1cb394d498f.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					---
 | 
				
			||||||
 | 
					features:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - Provides API 2.17, which makes the use of project_ids in API urls
 | 
				
			||||||
 | 
					    optional.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					upgrade:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - In order to make project_id optional in urls, we must constrain
 | 
				
			||||||
 | 
					    the set of allowed values for project_id in our urls. This
 | 
				
			||||||
 | 
					    defaults to a regex of ``[0-9a-f\-]+``, which will match hex uuids
 | 
				
			||||||
 | 
					    (with / without dashes), and integers. This covers all known
 | 
				
			||||||
 | 
					    project_id formats in the wild.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    If your site uses other values for project_id, you can set a site
 | 
				
			||||||
 | 
					    specific validation with ``project_id_regex`` config variable.
 | 
				
			||||||
		Reference in New Issue
	
	Block a user