os-loganalyze/os_loganalyze/wsgi.py

134 lines
4.5 KiB
Python
Executable File

#!/usr/bin/env python
#
# Copyright (c) 2013 IBM Corp.
#
# 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.
import cgi
import ConfigParser
import fileinput
import os.path
import sys
import os_loganalyze.filter as osfilter
import os_loganalyze.generator as osgen
import os_loganalyze.util as util
import os_loganalyze.view as osview
def htmlify_stdin():
out = sys.stdout
gen = osview.HTMLView(fileinput.FileInput())
for line in gen:
out.write(line)
def should_be_html(environ):
"""Simple content negotiation.
If the client supports content negotiation, and asks for text/html,
we give it to them, unless they also specifically want to override
by passing ?content-type=text/plain in the query.
This should be able to handle the case of dumb clients defaulting to
html, but also let devs override the text format when 35 MB html
log files kill their browser (as per a nova-api log).
"""
text_override = False
accepts_html = ('HTTP_ACCEPT' in environ and
'text/html' in environ['HTTP_ACCEPT'])
parameters = cgi.parse_qs(environ.get('QUERY_STRING', ''))
if 'content-type' in parameters:
ct = cgi.escape(parameters['content-type'][0])
if ct == 'text/plain':
text_override = True
return accepts_html and not text_override
def get_config(wsgi_config):
config = ConfigParser.ConfigParser()
config.read(os.path.expanduser(wsgi_config))
return config
def use_passthrough_view(file_headers):
"""Determine if we need to use the passthrough filter."""
if 'content-type' not in file_headers:
# For legacy we'll try and format. This shouldn't occur though.
return False
else:
if file_headers['content-type'] in ['text/plain', 'text/html']:
# We want to format these files
return False
if file_headers['content-type'] in ['application/x-gzip',
'application/gzip']:
# We'll need to guess if we should render the output or offer a
# download.
filename = file_headers['filename']
filename = filename[:-3] if filename[-3:] == '.gz' else filename
if os.path.splitext(filename)[1] in ['.txt', '.html']:
return False
return True
def application(environ, start_response, root_path=None,
wsgi_config='/etc/os_loganalyze/wsgi.conf'):
if root_path is None:
root_path = environ.get('OS_LOGANALYZE_ROOT_PATH',
'/srv/static/logs')
config = get_config(wsgi_config)
# make root path absolute in case we have a path with local components
# specified
root_path = os.path.abspath(root_path)
status = '200 OK'
try:
file_generator = osgen.get_file_generator(environ, root_path, config)
except osgen.UnsafePath:
status = '400 Bad Request'
response_headers = [('Content-type', 'text/plain')]
start_response(status, response_headers)
return ['Invalid file url']
except osgen.NoSuchFile:
status = "404 Not Found"
response_headers = [('Content-type', 'text/plain')]
start_response(status, response_headers)
return ['File Not Found']
if use_passthrough_view(file_generator.file_headers):
view_generator = osview.PassthroughView(file_generator)
else:
minsev = util.parse_param(environ, 'level', default="NONE")
limit = util.parse_param(environ, 'limit')
flines_generator = osfilter.Filter(file_generator, minsev, limit)
if environ.get('OS_LOGANALYZE_STRIP', None):
flines_generator.strip_control = True
if should_be_html(environ):
view_generator = osview.HTMLView(flines_generator)
else:
view_generator = osview.TextView(flines_generator)
start_response(status, view_generator.headers)
return view_generator
# for development purposes, makes it easy to test the filter output
if __name__ == "__main__":
htmlify_stdin()