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
This commit is contained in:
Brad Pokorny 2013-10-29 15:55:26 +00:00
parent c99ac7a7fe
commit ecdbcfb40f
6 changed files with 228 additions and 0 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -22,6 +22,7 @@ module=log
module=log_handler
module=loopingcall
module=memorycache
module=middleware
module=network_utils
module=notifier
module=periodic_task

View File

@ -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