Merge "Add x-openstack-request-id to nova v3 responses"
This commit is contained in:
+11
-5
@@ -65,14 +65,20 @@ use = call:nova.api.openstack.urlmap:urlmap_factory
|
||||
|
||||
[composite:openstack_compute_api_v2]
|
||||
use = call:nova.api.auth:pipeline_factory
|
||||
noauth = faultwrap sizelimit noauth ratelimit osapi_compute_app_v2
|
||||
keystone = faultwrap sizelimit authtoken keystonecontext ratelimit osapi_compute_app_v2
|
||||
keystone_nolimit = faultwrap sizelimit authtoken keystonecontext osapi_compute_app_v2
|
||||
noauth = compute_req_id faultwrap sizelimit noauth ratelimit osapi_compute_app_v2
|
||||
keystone = compute_req_id faultwrap sizelimit authtoken keystonecontext ratelimit osapi_compute_app_v2
|
||||
keystone_nolimit = compute_req_id faultwrap sizelimit authtoken keystonecontext osapi_compute_app_v2
|
||||
|
||||
[composite:openstack_compute_api_v3]
|
||||
use = call:nova.api.auth:pipeline_factory_v3
|
||||
noauth = faultwrap sizelimit noauth_v3 osapi_compute_app_v3
|
||||
keystone = faultwrap sizelimit authtoken keystonecontext osapi_compute_app_v3
|
||||
noauth = request_id faultwrap sizelimit noauth_v3 osapi_compute_app_v3
|
||||
keystone = request_id faultwrap sizelimit authtoken keystonecontext osapi_compute_app_v3
|
||||
|
||||
[filter:request_id]
|
||||
paste.filter_factory = nova.openstack.common.middleware.request_id:RequestIdMiddleware.factory
|
||||
|
||||
[filter:compute_req_id]
|
||||
paste.filter_factory = nova.api.compute_req_id:ComputeReqIdMiddleware.factory
|
||||
|
||||
[filter:faultwrap]
|
||||
paste.filter_factory = nova.api.openstack:FaultWrapper.factory
|
||||
|
||||
+5
-1
@@ -24,6 +24,7 @@ from nova import context
|
||||
from nova.openstack.common.gettextutils import _
|
||||
from nova.openstack.common import jsonutils
|
||||
from nova.openstack.common import log as logging
|
||||
from nova.openstack.common.middleware import request_id
|
||||
from nova import wsgi
|
||||
|
||||
|
||||
@@ -113,6 +114,8 @@ class NovaKeystoneContext(wsgi.Middleware):
|
||||
project_name = req.headers.get('X_TENANT_NAME')
|
||||
user_name = req.headers.get('X_USER_NAME')
|
||||
|
||||
req_id = req.environ.get(request_id.ENV_REQUEST_ID)
|
||||
|
||||
# Get the auth token
|
||||
auth_token = req.headers.get('X_AUTH_TOKEN',
|
||||
req.headers.get('X_STORAGE_TOKEN'))
|
||||
@@ -138,7 +141,8 @@ class NovaKeystoneContext(wsgi.Middleware):
|
||||
roles=roles,
|
||||
auth_token=auth_token,
|
||||
remote_address=remote_address,
|
||||
service_catalog=service_catalog)
|
||||
service_catalog=service_catalog,
|
||||
request_id=req_id)
|
||||
|
||||
req.environ['nova.context'] = ctx
|
||||
return self.application
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
# Copyright (c) 2014 IBM Corp.
|
||||
# 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.
|
||||
|
||||
"""Middleware that ensures x-compute-request-id
|
||||
|
||||
Using this middleware provides a convenient way to attach the
|
||||
x-compute-request-id to only v2 responses. Previously, this header was set in
|
||||
api/openstack/wsgi.py
|
||||
|
||||
Responses for APIv3 are taken care of by the request_id middleware provided
|
||||
in oslo.
|
||||
"""
|
||||
|
||||
import webob.dec
|
||||
|
||||
from nova.openstack.common import context
|
||||
from nova.openstack.common.middleware import base
|
||||
|
||||
|
||||
ENV_REQUEST_ID = 'openstack.request_id'
|
||||
HTTP_RESP_HEADER_REQUEST_ID = 'x-compute-request-id'
|
||||
|
||||
|
||||
class ComputeReqIdMiddleware(base.Middleware):
|
||||
|
||||
@webob.dec.wsgify
|
||||
def __call__(self, req):
|
||||
req_id = context.generate_request_id()
|
||||
req.environ[ENV_REQUEST_ID] = req_id
|
||||
response = req.get_response(self.application)
|
||||
if HTTP_RESP_HEADER_REQUEST_ID not in response.headers:
|
||||
response.headers.add(HTTP_RESP_HEADER_REQUEST_ID, req_id)
|
||||
return response
|
||||
@@ -1013,9 +1013,6 @@ class Resource(wsgi.Application):
|
||||
self.default_serializers)
|
||||
|
||||
if hasattr(response, 'headers'):
|
||||
if context:
|
||||
response.headers.add('x-compute-request-id',
|
||||
context.request_id)
|
||||
|
||||
for hdr, val in response.headers.items():
|
||||
# Headers must be utf-8 strings
|
||||
@@ -1238,7 +1235,6 @@ class Fault(webob.exc.HTTPException):
|
||||
|
||||
self.wrapped_exc.body = serializer.serialize(fault_data)
|
||||
self.wrapped_exc.content_type = content_type
|
||||
_set_request_id_header(req, self.wrapped_exc.headers)
|
||||
|
||||
return self.wrapped_exc
|
||||
|
||||
@@ -1298,9 +1294,3 @@ class RateLimitFault(webob.exc.HTTPException):
|
||||
self.wrapped_exc.content_type = content_type
|
||||
|
||||
return self.wrapped_exc
|
||||
|
||||
|
||||
def _set_request_id_header(req, headers):
|
||||
context = req.environ.get('nova.context')
|
||||
if context:
|
||||
headers['x-compute-request-id'] = context.request_id
|
||||
|
||||
@@ -20,7 +20,6 @@ import webob.exc
|
||||
|
||||
from nova.api import openstack as openstack_api
|
||||
from nova.api.openstack import wsgi
|
||||
import nova.context
|
||||
from nova import exception
|
||||
from nova.openstack.common import jsonutils
|
||||
from nova import test
|
||||
@@ -187,13 +186,3 @@ class APITest(test.NoDBTestCase):
|
||||
api = self._wsgi_app(fail)
|
||||
resp = webob.Request.blank('/').get_response(api)
|
||||
self.assertEqual(500, resp.status_int)
|
||||
|
||||
def test_request_id_in_response(self):
|
||||
req = webob.Request.blank('/')
|
||||
req.method = 'GET'
|
||||
context = nova.context.RequestContext('bob', 1)
|
||||
context.request_id = 'test-req-id'
|
||||
req.environ['nova.context'] = context
|
||||
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(res.headers['x-compute-request-id'], 'test-req-id')
|
||||
|
||||
@@ -335,6 +335,14 @@ class XMLDeserializerTest(test.NoDBTestCase):
|
||||
|
||||
|
||||
class ResourceTest(test.NoDBTestCase):
|
||||
|
||||
def get_req_id_header_name(self, request):
|
||||
header_name = 'x-openstack-request-id'
|
||||
if utils.get_api_version(request) < 3:
|
||||
header_name = 'x-compute-request-id'
|
||||
|
||||
return header_name
|
||||
|
||||
def test_resource_call_with_method_get(self):
|
||||
class Controller(object):
|
||||
def index(self, req):
|
||||
@@ -639,8 +647,6 @@ class ResourceTest(test.NoDBTestCase):
|
||||
context = req.environ['nova.context']
|
||||
app = fakes.TestRouter(Controller())
|
||||
response = req.get_response(app)
|
||||
self.assertEqual(response.headers['x-compute-request-id'],
|
||||
context.request_id)
|
||||
self.assertEqual(response.body, '{"foo": "bar"}')
|
||||
self.assertEqual(response.status_int, 200)
|
||||
|
||||
@@ -655,7 +661,8 @@ class ResourceTest(test.NoDBTestCase):
|
||||
# NOTE(alaski): This test is really to ensure that a str response
|
||||
# doesn't error. Not having a request_id header is a side effect of
|
||||
# our wsgi setup, ideally it would be there.
|
||||
self.assertFalse(hasattr(response.headers, 'x-compute-request-id'))
|
||||
expected_header = self.get_req_id_header_name(req)
|
||||
self.assertFalse(hasattr(response.headers, expected_header))
|
||||
self.assertEqual(response.body, 'foo')
|
||||
self.assertEqual(response.status_int, 200)
|
||||
|
||||
@@ -668,8 +675,6 @@ class ResourceTest(test.NoDBTestCase):
|
||||
context = req.environ['nova.context']
|
||||
app = fakes.TestRouter(Controller())
|
||||
response = req.get_response(app)
|
||||
self.assertEqual(response.headers['x-compute-request-id'],
|
||||
context.request_id)
|
||||
self.assertEqual(response.body, '')
|
||||
self.assertEqual(response.status_int, 200)
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import webob.exc
|
||||
|
||||
import nova.api.auth
|
||||
from nova.openstack.common.gettextutils import _
|
||||
from nova.openstack.common.middleware import request_id
|
||||
from nova import test
|
||||
|
||||
CONF = cfg.CONF
|
||||
@@ -70,6 +71,14 @@ class TestNovaKeystoneContextMiddleware(test.NoDBTestCase):
|
||||
response = self.request.get_response(self.middleware)
|
||||
self.assertEqual(response.status, '500 Internal Server Error')
|
||||
|
||||
def test_request_id_extracted_from_env(self):
|
||||
req_id = 'dummy-request-id'
|
||||
self.request.headers['X_PROJECT_ID'] = 'testtenantid'
|
||||
self.request.headers['X_USER_ID'] = 'testuserid'
|
||||
self.request.environ[request_id.ENV_REQUEST_ID] = req_id
|
||||
self.request.get_response(self.middleware)
|
||||
self.assertEqual(req_id, self.context.request_id)
|
||||
|
||||
|
||||
class TestKeystoneMiddlewareRoles(test.NoDBTestCase):
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
# Copyright (c) 2014 IBM Corp.
|
||||
# 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 testtools import matchers
|
||||
import webob
|
||||
import webob.dec
|
||||
|
||||
from nova.api import compute_req_id
|
||||
from nova.openstack.common import context
|
||||
from nova import test
|
||||
|
||||
|
||||
class RequestIdTest(test.TestCase):
|
||||
def test_generate_request_id(self):
|
||||
@webob.dec.wsgify
|
||||
def application(req):
|
||||
return req.environ[compute_req_id.ENV_REQUEST_ID]
|
||||
|
||||
app = compute_req_id.ComputeReqIdMiddleware(application)
|
||||
req = webob.Request.blank('/test')
|
||||
req_id = context.generate_request_id()
|
||||
req.environ[compute_req_id.ENV_REQUEST_ID] = req_id
|
||||
res = req.get_response(app)
|
||||
|
||||
res_id = res.headers.get(compute_req_id.HTTP_RESP_HEADER_REQUEST_ID)
|
||||
self.assertThat(res_id, matchers.StartsWith('req-'))
|
||||
self.assertEqual(res_id, res.body)
|
||||
@@ -206,3 +206,9 @@ def is_ipv6_supported():
|
||||
has_ipv6_support = False
|
||||
|
||||
return has_ipv6_support
|
||||
|
||||
|
||||
def get_api_version(request):
|
||||
api_version = 2
|
||||
if request.path[2:3].isdigit():
|
||||
return int(request.path[2:3])
|
||||
|
||||
Reference in New Issue
Block a user