[composite:neutronapi_v2_0]
use = call:neutron.auth:pipeline_factory
-noauth = extensions neutronapiapp_v2_0
-keystone = authtoken keystonecontext extensions neutronapiapp_v2_0
+noauth = request_id catch_errors extensions neutronapiapp_v2_0
+keystone = request_id catch_errors authtoken keystonecontext extensions neutronapiapp_v2_0
+
+[filter:request_id]
+paste.filter_factory = neutron.openstack.common.middleware.request_id:RequestIdMiddleware.factory
+
+[filter:catch_errors]
+paste.filter_factory = neutron.openstack.common.middleware.catch_errors:CatchErrorsMiddleware.factory
[filter:keystonecontext]
paste.filter_factory = neutron.auth:NeutronKeystoneContext.factory
from neutron import context
from neutron.openstack.common import log as logging
+from neutron.openstack.common.middleware import request_id
from neutron import wsgi
LOG = logging.getLogger(__name__)
tenant_name = req.headers.get('X_PROJECT_NAME')
user_name = req.headers.get('X_USER_NAME')
+ # Use request_id if already set
+ req_id = req.environ.get(request_id.ENV_REQUEST_ID)
+
# Create a context with the authentication data
ctx = context.Context(user_id, tenant_id, roles=roles,
- user_name=user_name, tenant_name=tenant_name)
+ user_name=user_name, tenant_name=tenant_name,
+ request_id=req_id)
# Inject the context...
req.environ['neutron.context'] = ctx
--- /dev/null
+# Copyright (c) 2013 OpenStack Foundation
+# 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.
+
+"""
+Attach open standard audit information to request.environ
+
+AuditMiddleware filter should be place after Keystone's auth_token middleware
+in the pipeline so that it can utilise the information Keystone provides.
+
+"""
+from pycadf.audit import api as cadf_api
+
+from neutron.openstack.common.middleware import notifier
+
+
+class AuditMiddleware(notifier.RequestNotifier):
+
+ def __init__(self, app, **conf):
+ super(AuditMiddleware, self).__init__(app, **conf)
+ self.cadf_audit = cadf_api.OpenStackAuditApi()
+
+ @notifier.log_and_ignore_error
+ def process_request(self, request):
+ self.cadf_audit.append_audit_event(request)
+ super(AuditMiddleware, self).process_request(request)
+
+ @notifier.log_and_ignore_error
+ def process_response(self, request, response,
+ exception=None, traceback=None):
+ self.cadf_audit.mod_audit_event(request, response)
+ super(AuditMiddleware, self).process_response(request, response,
+ exception, traceback)
--- /dev/null
+# Copyright 2011 OpenStack Foundation.
+# 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.
+
+"""Base class(es) for WSGI Middleware."""
+
+import webob.dec
+
+
+class Middleware(object):
+ """Base WSGI middleware wrapper.
+
+ These classes require an application to be initialized that will be called
+ next. By default the middleware will simply call its wrapped app, or you
+ can override __call__ to customize its behavior.
+ """
+
+ @classmethod
+ def factory(cls, global_conf, **local_conf):
+ """Factory method for paste.deploy."""
+ return cls
+
+ def __init__(self, application):
+ self.application = application
+
+ def process_request(self, req):
+ """Called on each request.
+
+ If this returns None, the next application down the stack will be
+ executed. If it returns a response then that response will be returned
+ and execution will stop here.
+ """
+ return None
+
+ def process_response(self, response):
+ """Do whatever you'd like to the response."""
+ return response
+
+ @webob.dec.wsgify
+ def __call__(self, req):
+ response = self.process_request(req)
+ if response:
+ return response
+ response = req.get_response(self.application)
+ return self.process_response(response)
--- /dev/null
+# Copyright (c) 2013 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.
+
+"""Middleware that provides high-level error handling.
+
+It catches all exceptions from subsequent applications in WSGI pipeline
+to hide internal errors from API response.
+"""
+
+import webob.dec
+import webob.exc
+
+from neutron.openstack.common.gettextutils import _ # noqa
+from neutron.openstack.common import log as logging
+from neutron.openstack.common.middleware import base
+
+
+LOG = logging.getLogger(__name__)
+
+
+class CatchErrorsMiddleware(base.Middleware):
+
+ @webob.dec.wsgify
+ def __call__(self, req):
+ try:
+ response = req.get_response(self.application)
+ except Exception:
+ LOG.exception(_('An error occurred during '
+ 'processing the request: %s'))
+ response = webob.exc.HTTPInternalServerError()
+ return response
--- /dev/null
+# Copyright (c) 2013 Rackspace Hosting
+# 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 attaches a correlation id to WSGI request"""
+
+import uuid
+
+from neutron.openstack.common.middleware import base
+
+
+class CorrelationIdMiddleware(base.Middleware):
+
+ def process_request(self, req):
+ correlation_id = (req.headers.get("X_CORRELATION_ID") or
+ str(uuid.uuid4()))
+ req.headers['X_CORRELATION_ID'] = correlation_id
--- /dev/null
+# Copyright 2011 OpenStack Foundation.
+# 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.
+
+"""Debug middleware"""
+
+from __future__ import print_function
+
+import sys
+
+import six
+import webob.dec
+
+from neutron.openstack.common.middleware import base
+
+
+class Debug(base.Middleware):
+ """Helper class that returns debug information.
+
+ Can be inserted into any WSGI application chain to get information about
+ the request and response.
+ """
+
+ @webob.dec.wsgify
+ def __call__(self, req):
+ print(("*" * 40) + " REQUEST ENVIRON")
+ for key, value in req.environ.items():
+ print(key, "=", value)
+ print()
+ resp = req.get_response(self.application)
+
+ print(("*" * 40) + " RESPONSE HEADERS")
+ for (key, value) in six.iteritems(resp.headers):
+ print(key, "=", value)
+ print()
+
+ resp.app_iter = self.print_generator(resp.app_iter)
+
+ return resp
+
+ @staticmethod
+ def print_generator(app_iter):
+ """Prints the contents of a wrapper string iterator when iterated."""
+ print(("*" * 40) + " BODY")
+ for part in app_iter:
+ sys.stdout.write(part)
+ sys.stdout.flush()
+ yield part
+ print()
--- /dev/null
+# Copyright (c) 2013 eNovance
+#
+# 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.
+
+"""
+Send notifications on request
+
+"""
+import os.path
+import sys
+import traceback as tb
+
+import six
+import webob.dec
+
+from neutron.openstack.common import context
+from neutron.openstack.common.gettextutils import _
+from neutron.openstack.common import log as logging
+from neutron.openstack.common.middleware import base
+from neutron.openstack.common.notifier import api
+
+LOG = logging.getLogger(__name__)
+
+
+def log_and_ignore_error(fn):
+ def wrapped(*args, **kwargs):
+ try:
+ return fn(*args, **kwargs)
+ except Exception as e:
+ LOG.exception(_('An exception occurred processing '
+ 'the API call: %s ') % e)
+ return wrapped
+
+
+class RequestNotifier(base.Middleware):
+ """Send notification on request."""
+
+ @classmethod
+ def factory(cls, global_conf, **local_conf):
+ """Factory method for paste.deploy."""
+ conf = global_conf.copy()
+ conf.update(local_conf)
+
+ def _factory(app):
+ return cls(app, **conf)
+ return _factory
+
+ def __init__(self, app, **conf):
+ self.service_name = conf.get('service_name', None)
+ self.ignore_req_list = [x.upper().strip() for x in
+ conf.get('ignore_req_list', '').split(',')]
+ super(RequestNotifier, self).__init__(app)
+
+ @staticmethod
+ def environ_to_dict(environ):
+ """Following PEP 333, server variables are lower case, so don't
+ include them.
+
+ """
+ return dict((k, v) for k, v in six.iteritems(environ)
+ if k.isupper())
+
+ @log_and_ignore_error
+ def process_request(self, request):
+ request.environ['HTTP_X_SERVICE_NAME'] = \
+ self.service_name or request.host
+ payload = {
+ 'request': self.environ_to_dict(request.environ),
+ }
+
+ api.notify(context.get_admin_context(),
+ api.publisher_id(os.path.basename(sys.argv[0])),
+ 'http.request',
+ api.INFO,
+ payload)
+
+ @log_and_ignore_error
+ def process_response(self, request, response,
+ exception=None, traceback=None):
+ payload = {
+ 'request': self.environ_to_dict(request.environ),
+ }
+
+ if response:
+ payload['response'] = {
+ 'status': response.status,
+ 'headers': response.headers,
+ }
+
+ if exception:
+ payload['exception'] = {
+ 'value': repr(exception),
+ 'traceback': tb.format_tb(traceback)
+ }
+
+ api.notify(context.get_admin_context(),
+ api.publisher_id(os.path.basename(sys.argv[0])),
+ 'http.response',
+ api.INFO,
+ payload)
+
+ @webob.dec.wsgify
+ def __call__(self, req):
+ if req.method in self.ignore_req_list:
+ return req.get_response(self.application)
+ else:
+ self.process_request(req)
+ try:
+ response = req.get_response(self.application)
+ except Exception:
+ exc_type, value, traceback = sys.exc_info()
+ self.process_response(req, None, value, traceback)
+ raise
+ else:
+ self.process_response(req, response)
+ return response
--- /dev/null
+# Copyright (c) 2013 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.
+
+"""Middleware that ensures request ID.
+
+It ensures to assign request ID for each API request and set it to
+request environment. The request ID is also added to API response.
+"""
+
+from neutron.openstack.common import context
+from neutron.openstack.common.middleware import base
+
+
+ENV_REQUEST_ID = 'openstack.request_id'
+HTTP_RESP_HEADER_REQUEST_ID = 'x-openstack-request-id'
+
+
+class RequestIdMiddleware(base.Middleware):
+
+ def process_request(self, req):
+ self.req_id = context.generate_request_id()
+ req.environ[ENV_REQUEST_ID] = self.req_id
+
+ def process_response(self, response):
+ response.headers.add(HTTP_RESP_HEADER_REQUEST_ID, self.req_id)
+ return response
--- /dev/null
+# Copyright (c) 2012 Red Hat, Inc.
+#
+# 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.
+
+"""
+Request Body limiting middleware.
+
+"""
+
+from oslo.config import cfg
+import webob.dec
+import webob.exc
+
+from neutron.openstack.common.gettextutils import _
+from neutron.openstack.common.middleware import base
+
+
+#default request size is 112k
+max_req_body_size = cfg.IntOpt('max_request_body_size',
+ deprecated_name='osapi_max_request_body_size',
+ default=114688,
+ help='the maximum body size '
+ 'per each request(bytes)')
+
+CONF = cfg.CONF
+CONF.register_opt(max_req_body_size)
+
+
+class LimitingReader(object):
+ """Reader to limit the size of an incoming request."""
+ def __init__(self, data, limit):
+ """Initiates LimitingReader object.
+
+ :param data: Underlying data object
+ :param limit: maximum number of bytes the reader should allow
+ """
+ self.data = data
+ self.limit = limit
+ self.bytes_read = 0
+
+ def __iter__(self):
+ for chunk in self.data:
+ self.bytes_read += len(chunk)
+ if self.bytes_read > self.limit:
+ msg = _("Request is too large.")
+ raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg)
+ else:
+ yield chunk
+
+ def read(self, i=None):
+ result = self.data.read(i)
+ self.bytes_read += len(result)
+ if self.bytes_read > self.limit:
+ msg = _("Request is too large.")
+ raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg)
+ return result
+
+
+class RequestBodySizeLimiter(base.Middleware):
+ """Limit the size of incoming requests."""
+
+ @webob.dec.wsgify
+ def __call__(self, req):
+ if req.content_length > CONF.max_request_body_size:
+ msg = _("Request is too large.")
+ raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg)
+ if req.content_length is None and req.is_body_readable:
+ limiter = LimitingReader(req.body_file,
+ CONF.max_request_body_size)
+ req.body_file = limiter
+ return self.application
import webob
from neutron import auth
+from neutron.openstack.common.middleware import request_id
from neutron.tests import base
self.assertEqual(self.context.user_name, 'testusername')
self.assertEqual(self.context.tenant_id, 'testtenantid')
self.assertEqual(self.context.tenant_name, 'testtenantname')
+
+ 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)
module=log
module=log_handler
module=loopingcall
+module=middleware
module=network_utils
module=notifier
module=periodic_task