#!/usr/bin/env python
#from optparse import OptionParser, Option
import time, sys, os, glob
import getopt, re
#__cmdParser__ = OptionParser()
#__cmdParser__.add_option("-i", "--input", action="append",
# dest="logfiles", type="string", metavar = "FILE",
# help="system log to parse")
#__cmdParser__.add_option("-v", "--verbose", action="count",
# dest="verbosity",
# help="How obnoxious we're being about telling the user what we're doing.")
#(__cmdLineOpts__, __cmdLineArgs__)=__cmdParser__.parse_args()
class htmlgen_class:
def __init__(self):
self.html_fname_base = "/tmp/something"
self.page_num = 0
self.page_line = 0
self.page_fp = None
self.cur_time = None
self.old_time = None
self.summary_fp = open(self.html_fname_base + ".html", "w")
self.summary_fp.write("<html><body><ul>")
def new_page(self):
# close previous page
if self.page_fp:
self.close_page()
self.page_fp = open( "%s_%d.html" % (self.html_fname_base, self.page_num), "w")
self.page_num += 1
self.page_line = 0
self.page_fp.write("""<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title></title>
<style type="text/css">
<!--
body {
font: normal 11px auto "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
color: #4f6b72;
background: #E6EAE9;
}
a {
color: #c75f3e;
}
#mytable {
width: 97%;
padding: 0;
margin: 0;
}
caption {
padding: 0 0 5px 0;
width: 97%;
font: italic 11px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
text-align: right;
}
th {
font: bold 11px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
color: #4f6b72;
letter-spacing: 2px;
text-transform: uppercase;
text-align: left;
padding: 6px 6px 6px 12px;
background: #a50000 url(images/bg_header.jpg) no-repeat;
color: white;
}
th.nobg {
border-top: 0;
border-left: 0;
border-right: 1px solid #C1DAD7;
background: none;
color: black;
}
td {
border-right: 1px solid #C1DAD7;
border-bottom: 1px solid #C1DAD7;
background: #fff;
padding: 6px 6px 6px 12px;
color: #4f6b72;
letter-spacing: -1px;
white-space:pre-wrap;
font-family: monospace;
}
pre {
white-space: pre-wrap; /* css-3 */
white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
}
td.alt {
background: #ffbaba;
color: #797268;
}
th.newday {
text-align: right;
padding: 2px;
}
th.spec {
border-left: 1px solid #C1DAD7;
border-right: 1px solid #C1DAD7;
border-top: 0;
background: #fff url(images/bullet1.gif) no-repeat;
font: bold 10px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
color: #797268;
}
th.specalt {
border-left: 1px solid #C1DAD7;
border-right: 1px solid #C1DAD7;
border-top: 0;
background: #cecfce url(images/bullet2.gif) no-repeat;
font: bold 10px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
color: #797268;
}
-->
</style>
</head>
<body>
<table id="mytable" cellspacing="0" summary="The technical specifications of the Apple PowerMac G5 series">
<caption>Generated by SOS htmlogger 1.0 on March 16th 2008</caption>
""")
def new_line(self, new_time, events = []):
self.old_time = self.cur_time
self.cur_time = new_time
# close previous line
if self.page_line > 0:
self.page_fp.write("</TR>")
if self.page_line == 0 or self.page_line % 200 == 0:
self.page_fp.write("""<tr><th scope="col" abbr="Date/Time" class="nobg">Time</th><th scope="col" abbr="node-1">node-1</th><th scope="col" abbr="node-2">node-2</th><th scope="col" abbr="node-3">node-3</th></tr>""")
if self.page_line == 0 or self.page_line % 200 == 0 or time.strftime("%b %d", self.old_time) != time.strftime("%b %d", new_time):
self.page_fp.write("""<tr><th scope="row" class="newday" colspan=4>%s</th></tr>""" % time.strftime("%A, %B %d", new_time))
if self.page_line % 2 == 0: row_class = "spec"
else: row_class = "specalt"
if len(events) > 0:
stime = """<a name="row_%d">%s</a>""" % (self.page_line, time.strftime("%H:%M:%S", new_time))
for event in events:
self.summary_fp.write('<li><a href="something_%d.html#row_%s">%s</a>' % (self.page_num-1,self.page_line,event) )
else:
stime = time.strftime("%H:%M:%S", new_time)
if not self.old_time or self.old_time != new_time:
self.page_fp.write("""<TR><th scope="row" class="%s">%s</th>""" % (row_class, stime))
else:
self.page_fp.write("""<TR><th scope="row" class="%s" style="color: #cacaca">%s</th>""" % (row_class, stime))
self.page_line+=1
def new_tab(self,msg):
self.page_fp.write(" <TD>" + msg + "</TD>\n")
def close_page(self):
self.page_fp.write("</table></html>\n")
self.page_fp.close()
def finish(self):
self.close_page()
class host_class:
def __init__(self):
self.logs = []
self.log_idx = 0 # first log
self.log_ptr = 0 # first char
self.cur_line = None
def add_log(self, logfile):
# if not logfile == logfile_class:
# raise "InvalidLogfile"
for inc in range(0,len(self.logs)):
if logfile.time_end() < self.logs[inc].time_begin():
self.logs.insert(inc, logfile)
break
else:
self.logs.append(logfile)
def hostname(self):
try: return self.logs[0].hostname()
except: return None
def tell(self):
sumsize = 0
if self.log_idx > 0:
for inc in range(0, self.log_idx):
sumsize += self.logs[inc].size()
try:
sumsize += self.fp().tell()
except TypeError:
pass
return sumsize
def size(self):
sumsize = 0
for inc in range(0, len(self.logs)):
sumsize += self.logs[inc].size()
return sumsize
def eof(self):
if self.tell() >= self.size():
return True
return False
def seek(self, offset, whence = 0):
if whence == 1: offset = self.tell() + offset
elif whence == 2: offset = self.size() + offset
sumsize = 0
for inc in range(0, len(self.logs)):
if offset <= sumsize + self.logs[inc].size():
offset -= sumsize
self.log_idx = inc
self.log_ptr = offset
self.logs[inc].seek(offset)
return True
sumsize += self.logs[inc].size()
raise "Off_Boundaries"
def time(self):
return time.strptime(self.cur_line[0:15], "%b %d %H:%M:%S")
pos = self.tell()
try:
toret = time.strptime(self.readline()[0:15], "%b %d %H:%M:%S")
except ValueError:
toret = None
self.seek(pos)
return toret
def fp(self):
return self.logs[self.log_idx]
def backline(self):
self.seek(-len(self.cur_line), 1)
def readline(self):
if self.eof():
return ""
while True:
toret = self.fp().readline()
if len(toret) == 0:
if self.log_idx < len(self.logs):
self.log_idx += 1
self.fp().seek(0)
continue
else:
return ""
if self.validate_line(toret) or toret == "":
self.cur_line = toret
return toret
else:
print "invalid line"
def validate_line(self, line):
try:
time.strptime(line[0:15], "%b %d %H:%M:%S")
except:
return False
return True
def cur_msg(self):
if not hasattr(self,"_regex_cur_msg"):
self._regex_cur_msg = re.compile(r"""^.* %s (\S+:.*)$""" % self.hostname())
try: return self._regex_cur_msg.findall(self.cur_line)[0]
except: return self.cur_line
class logfile_class:
def __init__(self,fname):
self.events = []
self.fname = fname
self.fp = open(fname)
def hostname(self):
pos = self.fp.tell()
toret = self.fp.readline()[16:].split(" ")[0]
self.fp.seek(pos)
return toret
def time_begin(self):
pos = self.fp.tell()
self.fp.seek(0)
toret = time.strptime(self.fp.readline()[0:15], "%b %d %H:%M:%S")
self.fp.seek(pos)
return toret
def time_end(self):
pos = self.fp.tell()
bs = 1024
if self.size() < bs: bs = self.size()
self.fp.seek(-bs, 2)
line = self.fp.read(bs)
toret = time.strptime(line[line.rfind("\n", 0, bs - 1) + 1:][0:15], "%b %d %H:%M:%S")
self.fp.seek(pos)
return toret
def size(self):
return os.path.getsize(self.fname)
def eof(self):
return self.fp.tell() > self.size()
def readline(self):
return self.fp.readline()
def seek(self,pos):
self.fp.seek(pos)
def tell(self):
return self.fp.tell()
def validate_line(self):
return self.time_current(self.curmessage())
def time_current(self):
if len(self.curline) == 0: return None
try:
return time.strptime(self.curline[0:15], "%b %d %H:%M:%S")
except ValueError:
print "could not parse time", self.curline
return False
def usage():
print "ciao"
try:
opts, args = getopt.getopt(sys.argv[1:], "hi:v", ["help", "input="])
except getopt.GetoptError:
# print help information and exit:
usage()
sys.exit(2)
cmdline = {}
cmdline["logfiles"] = []
for o, a in opts:
if o == "-v":
verbose = True
if o in ("-h", "--help"):
usage()
sys.exit()
if o in ("-i", "--input"):
print o,a
for fname in sys.argv[2:]:
cmdline["logfiles"].append(fname)
sys.stderr.write("adding log %s\n" % fname)
hosts = {}
for logname in cmdline["logfiles"]:
log = logfile_class(logname)
hostname = log.hostname()
sys.stderr.write("log %s for host %s\n" % (logname, hostname))
if not hosts.has_key(hostname):
hosts[hostname] = host_class()
hosts[hostname].add_log(log)
sys.stderr.write("finished adding logs\n")
#print '<ul id="toc">'
#for log in logs:
# print logs[idx].fname, logs[idx].events
# for line, msglen, event in log.events:
# print ' <li><span>%s</span> <a href="#">Link</a><br /></li>' % event
htmlgen = htmlgen_class()
previous_date = None
while True:
# who is next ?
lowest_date = None
for host in hosts:
if hosts[host].eof():
continue
hosts[host].readline()
if lowest_date == None or hosts[host].time() < lowest_date:
lowest_date = hosts[host].time()
if lowest_date == None:
# all logs are EOF
break
events = []
for host in hosts:
if hosts[host].time() == lowest_date:
if re.match(r'^kernel: Linux version', hosts[host].cur_msg()):
events.append("%s rebooted" % host)
elif re.match(r'^.*fencing node', hosts[host].cur_msg()):
events.append(host + " " + hosts[host].cur_msg())
elif re.match(r'.*fence ".*" success', hosts[host].cur_msg()):
events.append(host + " " + hosts[host].cur_msg())
elif re.match(r'.*fence ".*" failed', hosts[host].cur_msg()):
events.append(host + " " + hosts[host].cur_msg())
elif re.match(r'.*quorum lost, blocking activity', hosts[host].cur_msg()):
events.append(host + " " + hosts[host].cur_msg())
elif re.match(r'.*quorum regained, resuming activity', hosts[host].cur_msg()):
events.append(host + " " + hosts[host].cur_msg())
elif re.match(r'.*segfault at', hosts[host].cur_msg()):
events.append(host + " " + hosts[host].cur_msg())
elif not hosts[host].eof():
hosts[host].backline()
if len(events): print time.strftime("%A %B %d %H:%M:%S", lowest_date), events
if htmlgen.page_line == 0 or htmlgen.page_line > 10000:
print "creating new page #%d" % htmlgen.page_num
htmlgen.new_page()
htmlgen.new_line(lowest_date, events)
for host in hosts:
if hosts[host].time() == lowest_date:
htmlgen.new_tab(hosts[host].cur_msg())
else:
htmlgen.new_tab("")
htmlgen.close_page()