blob: 52a5a666ebc42f90e74daaa83bdba0c0ca0aa249 [file] [log] [blame]
Sean Daguebcdba082013-03-12 15:14:16 -04001#!/usr/bin/env python
2# vim: tabstop=4 shiftwidth=4 softtabstop=4
3
4# Copyright 2013 IBM Corp.
5# All Rights Reserved.
6#
7# Licensed under the Apache License, Version 2.0 (the "License"); you may
8# not use this file except in compliance with the License. You may obtain
9# a copy of the License at
10#
11# http://www.apache.org/licenses/LICENSE-2.0
12#
13# Unless required by applicable law or agreed to in writing, software
14# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16# License for the specific language governing permissions and limitations
17# under the License.
18
19import gzip
20import re
21import StringIO
22import sys
23import urllib2
24
Sean Dague70eef032013-03-20 13:41:15 -040025import pprint
26pp = pprint.PrettyPrinter()
27
28NOVA_TIMESTAMP = r"\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\.\d\d\d"
29
30NOVA_REGEX = r"(?P<timestamp>%s) (?P<pid>\d+ )?(?P<level>(ERROR|TRACE)) " \
31 "(?P<module>[\w\.]+) (?P<msg>.*)" % (NOVA_TIMESTAMP)
32
33
34class StackTrace(object):
35 timestamp = None
36 pid = None
37 level = ""
38 module = ""
39 msg = ""
40
41 def __init__(self, timestamp=None, pid=None, level="", module="",
42 msg=""):
43 self.timestamp = timestamp
44 self.pid = pid
45 self.level = level
46 self.module = module
47 self.msg = msg
48
49 def append(self, msg):
50 self.msg = self.msg + msg
51
52 def is_same(self, data):
53 return (data['timestamp'] == self.timestamp and
54 data['level'] == self.level)
55
56 def not_none(self):
57 return self.timestamp is not None
58
59 def __str__(self):
60 buff = "<%s %s %s>\n" % (self.timestamp, self.level, self.module)
61 for line in self.msg.splitlines():
62 buff = buff + line + "\n"
63 return buff
64
Sean Daguebcdba082013-03-12 15:14:16 -040065
66def hunt_for_stacktrace(url):
67 """Return TRACE or ERROR lines out of logs."""
David Kranz9e3c7172013-10-09 21:45:31 -040068 req = urllib2.Request(url)
69 req.add_header('Accept-Encoding', 'gzip')
70 page = urllib2.urlopen(req)
Sean Daguebcdba082013-03-12 15:14:16 -040071 buf = StringIO.StringIO(page.read())
72 f = gzip.GzipFile(fileobj=buf)
73 content = f.read()
Sean Dague70eef032013-03-20 13:41:15 -040074
75 traces = []
76 trace = StackTrace()
77 for line in content.splitlines():
78 m = re.match(NOVA_REGEX, line)
79 if m:
80 data = m.groupdict()
81 if trace.not_none() and trace.is_same(data):
82 trace.append(data['msg'] + "\n")
83 else:
84 trace = StackTrace(
85 timestamp=data.get('timestamp'),
86 pid=data.get('pid'),
87 level=data.get('level'),
88 module=data.get('module'),
89 msg=data.get('msg'))
90
91 else:
92 if trace.not_none():
93 traces.append(trace)
94 trace = StackTrace()
95
96 # once more at the end to pick up any stragglers
97 if trace.not_none():
98 traces.append(trace)
99
100 return traces
Sean Daguebcdba082013-03-12 15:14:16 -0400101
102
103def log_url(url, log):
104 return "%s/%s" % (url, log)
105
106
107def collect_logs(url):
108 page = urllib2.urlopen(url)
109 content = page.read()
110 logs = re.findall('(screen-[\w-]+\.txt\.gz)</a>', content)
111 return logs
112
113
114def usage():
Dirk Mueller1db5db22013-06-23 20:21:32 +0200115 print("""
Sean Daguebcdba082013-03-12 15:14:16 -0400116Usage: find_stack_traces.py <logurl>
117
118Hunts for stack traces in a devstack run. Must provide it a base log url
119from a tempest devstack run. Should start with http and end with /logs/.
120
121Returns a report listing stack traces out of the various files where
122they are found.
Dirk Mueller1db5db22013-06-23 20:21:32 +0200123""")
Sean Daguebcdba082013-03-12 15:14:16 -0400124 sys.exit(0)
125
126
Sean Dague70eef032013-03-20 13:41:15 -0400127def print_stats(items, fname, verbose=False):
128 errors = len(filter(lambda x: x.level == "ERROR", items))
129 traces = len(filter(lambda x: x.level == "TRACE", items))
Dirk Mueller1db5db22013-06-23 20:21:32 +0200130 print("%d ERRORS found in %s" % (errors, fname))
131 print("%d TRACES found in %s" % (traces, fname))
Sean Dague70eef032013-03-20 13:41:15 -0400132
133 if verbose:
134 for item in items:
Dirk Mueller1db5db22013-06-23 20:21:32 +0200135 print(item)
136 print("\n\n")
Sean Dague70eef032013-03-20 13:41:15 -0400137
138
Sean Daguebcdba082013-03-12 15:14:16 -0400139def main():
140 if len(sys.argv) == 2:
141 url = sys.argv[1]
142 loglist = collect_logs(url)
143
144 # probably wrong base url
145 if not loglist:
146 usage()
147
148 for log in loglist:
149 logurl = log_url(url, log)
150 traces = hunt_for_stacktrace(logurl)
Sean Dague70eef032013-03-20 13:41:15 -0400151
Sean Daguebcdba082013-03-12 15:14:16 -0400152 if traces:
Sean Dague70eef032013-03-20 13:41:15 -0400153 print_stats(traces, log, verbose=True)
154
Sean Daguebcdba082013-03-12 15:14:16 -0400155 else:
156 usage()
157
158if __name__ == '__main__':
159 main()