From 8c0c9b1169b9cc1763015bcec5fc742ca82248e9 Mon Sep 17 00:00:00 2001 From: Brad Pokorny Date: Tue, 29 Oct 2013 15:55:26 +0000 Subject: [PATCH] Sync middleware audit, base, and notifier from oslo Pull in middleware modules required to support CADF auditing reports from Nova. audit.py requires notify.py, and notify.py requires base.py. This is why those 3 files are being synced, and not others in the openstack/common/middleware directory from oslo. The following 3 change IDs included in this sync provide the main parts of the CADF functions being synced: Ie67cf4a7 Add audit middleware Iead2431a Middleware: add notifier I05ff58 Move wsgi module to deprecated package The following change IDs additionally included in this sync provide minor updates to the files that were made after the main parts of the CADF functions were merged in oslo: I032616 Changed header from LLC to Foundation based on trademark policies I840c43bf add support to ignore certain request I2099e1ee Enable hacking H404 test I04b0cf middleware: code cleanup blueprint support-standard-audit-formats Change-Id: Id15f95a0c354270ba2d5aecb12c4f910905e8c20 --- nova/openstack/common/middleware/__init__.py | 0 nova/openstack/common/middleware/audit.py | 45 +++++++ nova/openstack/common/middleware/base.py | 55 ++++++++ nova/openstack/common/middleware/notifier.py | 126 +++++++++++++++++++ openstack-common.conf | 1 + requirements.txt | 1 + 6 files changed, 228 insertions(+) create mode 100644 nova/openstack/common/middleware/__init__.py create mode 100644 nova/openstack/common/middleware/audit.py create mode 100644 nova/openstack/common/middleware/base.py create mode 100644 nova/openstack/common/middleware/notifier.py diff --git a/nova/openstack/common/middleware/__init__.py b/nova/openstack/common/middleware/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/nova/openstack/common/middleware/audit.py b/nova/openstack/common/middleware/audit.py new file mode 100644 index 000000000000..69a890f7af33 --- /dev/null +++ b/nova/openstack/common/middleware/audit.py @@ -0,0 +1,45 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# 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 nova.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) diff --git a/nova/openstack/common/middleware/base.py b/nova/openstack/common/middleware/base.py new file mode 100644 index 000000000000..20995498a30c --- /dev/null +++ b/nova/openstack/common/middleware/base.py @@ -0,0 +1,55 @@ +# 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) diff --git a/nova/openstack/common/middleware/notifier.py b/nova/openstack/common/middleware/notifier.py new file mode 100644 index 000000000000..5a0f5290f1c1 --- /dev/null +++ b/nova/openstack/common/middleware/notifier.py @@ -0,0 +1,126 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# 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 webob.dec + +from nova.openstack.common import context +from nova.openstack.common.gettextutils import _ # noqa +from nova.openstack.common import log as logging +from nova.openstack.common.middleware import base +from nova.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 environ.iteritems() + 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: + type, value, traceback = sys.exc_info() + self.process_response(req, None, value, traceback) + raise + else: + self.process_response(req, response) + return response diff --git a/openstack-common.conf b/openstack-common.conf index 4acd24e6d692..fda4ba040f2b 100644 --- a/openstack-common.conf +++ b/openstack-common.conf @@ -22,6 +22,7 @@ module=log module=log_handler module=loopingcall module=memorycache +module=middleware module=network_utils module=notifier module=periodic_task diff --git a/requirements.txt b/requirements.txt index 65c808d68bcd..0b21fe8bd42f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -29,3 +29,4 @@ six>=1.4.1 stevedore>=0.10 websockify>=0.5.1,<0.6 oslo.config>=1.2.0 +pycadf>=0.1.9