#
# reportMultiDowntime.py: Multiple downtime plotter, stripchart-style.
#
# Doug White
import os
import string
import time
import cPickle
import qosreport
import downtimer
import HtmlKit
import urllib
from englishUnits import *
class reportMultiDowntime(qosreport.QOSReport):
def displayOptionsInfo(self, request, url):
optionsinfo = [ [ 'Report Sections', [
('reportparams', 'Report Parameters', 1),
('summary', 'Summary', 1),
('graph', 'Graph', 1),
('links', 'Detail Links', 1),
],
],
[ 'Summary Options', [
('saltalg', 'Use "wallclock" Downtime Algorithm', 0),
],
],
[ 'Graph Options', [
('xtime', 'Plot X Axis as Dates (unchecked will plot as time)', 1),
],
],
]
self.displayOptions(request, url, optionsinfo)
# Return true if we aren't going through another pass.
def doneWithEnts(self, vars):
if vars.has_key('ents') and not vars.has_key('add'):
return 1
else:
return None
# figure out what page to display next (tricky!)
def displayPage(self, request, url):
vars = self.unquoteDict(url.argumentsDict())
if vars.has_key('agent'):
print "Testing %s: %s" % (vars['agent'], self.entityIsGroup(vars['agent']))
if vars.has_key('dograph'):
self.displayGraph(request, url)
elif vars.has_key('delete') or (vars.has_key('agent') and self.entityIsGroup(vars['agent'])):
self.displayMoreEnts(request, url)
elif not vars.has_key('agent') and not self.doneWithEnts(vars):
self.displayAgent(request, url)
elif not vars.has_key('entity') and not self.doneWithEnts(vars):
self.displayEntity(request, url)
elif not self.doneWithEnts(vars):
self.displayMoreEnts(request, url)
elif not vars.has_key('fromdate'):
self.displayDateRange(request, url)
elif not vars.has_key('graph') and not vars.has_key('detail') and not vars.has_key('summary') and not vars.has_key('reportparams'):
self.displayOptionsInfo(request, url)
else:
self.displayReport(request, url)
def displayMoreEnts(self, request, url):
vars = self.unquoteDict(url.argumentsDict())
ents = []
if vars.has_key('ents'):
ents = cPickle.loads(urllib.unquote(vars['ents']))
# See if we have a group in the list.
if not vars.has_key('delete'):
if self.entityIsGroup(vars['agent']):
ents.append('%s' % vars['agent'])
del url.argumentsDict()['agent']
# For everything else...
else:
ents.append('%s_%s' % ( vars['agent'], vars['entity']))
del url.argumentsDict()['agent']
del url.argumentsDict()['entity']
# delete marked items (heh, now) -- the slice is required to
# avoid confusing python's for operator. The Python Language Reference
# suggests this when doing nasty self-referential filters like this.
for line in ents[:]:
if url.argumentsDict().has_key(line):
print "deleting %s" % (line)
ents.remove(line)
# and remove the delete marker...
del url.argumentsDict()[line]
del vars[line]
# The double-quote here is to get around preserveState's unquoting
# print "Ents:", ents, urllib.quote(cPickle.dumps(ents))
url.argumentsDict()['ents'] = urllib.quote(urllib.quote(cPickle.dumps(ents, 0)))
# Get rid of the markers too
if url.argumentsDict().has_key('add'):
del url.argumentsDict()['add']
if url.argumentsDict().has_key('delete'):
del url.argumentsDict()['delete']
# Start page render
request['Content-Type'] = 'text/html'
p = HtmlKit.Page()
p.title_('QOS: Reports: %s: List' %
(self.typename))
p.append('<H1>QOS: <a href="/">Reports</a>: <a href="%s">%s</a>: List</H1>\n' %
(self.prefix, self.typename))
f = HtmlKit.Form(method='GET', action=self.prefix)
p.append(f)
self.preserveState(url.argumentsDict(), f)
p1 = HtmlKit.Panel()
p1.title_('Selected Entities')
p1t1 = HtmlKit.Table()
p1t1r1 = p1t1.newRow()
p1t1r1.newHeader('Delete')
p1t1r1.newHeader('Entity')
for line in ents:
p1t1c = p1t1.newRow()
p1t1c.newColumn(HtmlKit.Center(HtmlKit.Input(type='checkbox', name=line, value='1')))
p1t1c.newColumn(line)
p1.content().append(p1t1)
p1t2c = p1.table().newRow().newColumn()
p1t2c.append(HtmlKit.Button(value='Next >'))
p1t2c.append(HtmlKit.Button(name='add', value='Add'))
p1t2c.append(HtmlKit.Button(name='delete', value='Delete'))
f.append(p1)
# finish up
request.push(str(p))
request.done()
def displayReport(self, request, url):
# load variables
vars = self.unquoteDict(url.argumentsDict())
reportparams = self.loadVar(url.argumentsDict(), 'reportparams')
if vars.has_key('ents'):
ents = cPickle.loads(urllib.unquote(vars['ents']))
p = HtmlKit.Page()
p.title_('QOS: Reports: %s: Report' % self.typename)
p.append('<H1>QOS: Reports: Multiple Downtime Report')
f = HtmlKit.Form(method='GET', action=self.prefix)
p.append(f)
self.preserveState(url.argumentsDict(), f)
if reportparams:
### Parameters panel ###
p1 = HtmlKit.Panel()
p1.title_('Report Parameters')
pt1 = HtmlKit.Table()
pt1.newRow().newHeader('Entities', colspan=4)
pt1.newRow().newColumn(HtmlKit.Center(string.join(ents, ', ')), colspan=4).nowrap_(0)
pt1r3 = pt1.newRow()
pt1r4 = pt1.newRow()
pt1r3.newHeader('From')
pt1r4.newColumn(HtmlKit.Center("%s %s" % (vars['fromdate'], vars['fromtime'])))
pt1r3.newHeader('To')
pt1r4.newColumn(HtmlKit.Center("%s %s" % (vars['todate'], vars['totime'])))
if vars.has_key('summary') and vars.has_key('saltalg'):
pt1r3.newHeader('Summary Options')
optlist = []
if vars.has_key('saltalg'):
optlist.append('Wallclock Algorithm')
pt1r4.newColumn(HtmlKit.Center(string.join(optlist, ", ")))
if vars.has_key('graph') and vars.has_key('xtime'):
pt1r3.newHeader('Graph Options')
optlist=[]
if vars.has_key('xtime'):
optlist.append('X as Dates')
pt1r4.newColumn(HtmlKit.Center(string.join(optlist, ", ")))
p1.content().append(pt1)
f.append(p1)
f.append("<p>")
if vars.has_key('summary'):
### Summary section ###
# Here we want to run the summary data for each entity, then accumulate
# it into a final summary line.
sp1 = HtmlKit.Panel()
sp1.title_('Summary')
st1 = HtmlKit.Table()
st1r1 = st1.newRow()
st1r1.newHeader('Entity')
st1r1.newHeader('Uptime %')
st1r1.newHeader('Total Downtime')
st1r1.newHeader('Longest Downtime')
st1r1.newHeader('Average Downtime')
st1r1.newHeader('DT %')
cumdt = 0
avgdt = -1
cur = 0
result = []
for ent in ents:
cur = ents.index(ent)
if self.entityIsGroup(ent):
# here comes the group logic
# We want to loop over the group members and downtimer each one
# then combine() the results and print the output as one
(agent, entity, groupname) = string.split(ent, '_', 2)
groupents = self.ConfigFile['Groups'][self.typename][groupname]
groupdt = []
for gent in groupents:
(agent, entity) = string.split(gent, '_', 1)
dt = downtimer.Downtimer(agent=agent,
entity=entity,
startdate=vars['fromdate'],
starttime=vars['fromtime'],
enddate=vars['todate'],
endtime=vars['totime'])
groupdt.append(dt.run())
# now apply the glue
result.append(dt.combine(groupdt))
# for regular entities...
else:
(agent, entity) = string.split(ent, '_', 1)
dt = downtimer.Downtimer(agent=agent,
entity=entity,
startdate=vars['fromdate'],
starttime=vars['fromtime'],
enddate=vars['todate'],
endtime=vars['totime'])
result.append(dt.run())
# Common math here
cumdt = cumdt + result[cur][2]
if avgdt != -1:
avgdt = (avgdt + result[cur][4]) / 2
else :
avgdt = result[cur][4]
# Run thru the results, calc the contrib percents and print out
uniqresult = dt.combine(result)
# XXX This is NOT cumulative downtime .. it's the total downtime after
# flattening the data! Just toggling this changes the downtime calc
# algorithm between #1 and #2...
if vars.has_key('saltalg'):
cumdt = uniqresult[2]
avgdt = uniqresult[4]
for ent in ents:
cur = ents.index(ent)
st1r2 = st1.newRow()
st1r2.newColumn(HtmlKit.Center(ent))
# % uptime
st1r2.newColumn(HtmlKit.Center("%3.2f%%" % (result[cur][1] * 100)))
# total dt
st1r2.newColumn(HtmlKit.Center(string.join(english_time(int(result[cur][2]))[0:2])))
# longest dt
st1r2.newColumn(HtmlKit.Center(string.join(english_time(int(result[cur][3][1]))[0:2])))
# avg dt
st1r2.newColumn(HtmlKit.Center(string.join(english_time(int(result[cur][4]))[0:2])))
# longest ut
#st1r2.newColumn(HtmlKit.Center(string.join(english_time(int(result[5][1])))))
if cumdt > 0:
st1r2.newColumn(HtmlKit.Center("%3.2f%%" % (result[cur][2] / cumdt * 100)))
else:
st1r2.newColumn(HtmlKit.Center("0.00%"))
# final stats
st1r2 = st1.newRow()
if vars.has_key('saltalg'):
st1r2.newColumn(HtmlKit.Center(HtmlKit.Bold('Overall')))
else:
st1r2.newColumn(HtmlKit.Center(HtmlKit.Bold('Cumulative')))
st1r2.newColumn(HtmlKit.Center(HtmlKit.Bold("%3.2f%%" % (uniqresult[1] * 100))))
st1r2.newColumn(HtmlKit.Center(HtmlKit.Bold(string.join(english_time(int(cumdt))[0:2]))))
st1r2.newColumn(HtmlKit.Center(HtmlKit.Bold(string.join(english_time(int(uniqresult[3][1]))[0:2]))))
st1r2.newColumn(HtmlKit.Center(HtmlKit.Bold(string.join(english_time(int(avgdt))[0:2]))))
st1r2.newColumn(HtmlKit.Center(HtmlKit.Bold('100.00%')))
sp1.content().append(st1)
f.append(sp1)
f.append("<p>")
if vars.has_key('graph'):
# insert graph url
f.append(HtmlKit.Center(HtmlKit.Image(src="%s?%s&dograph=1" %
(self.prefix, urllib.unquote(url.argumentsString())))))
if vars.has_key('links'):
p2 = HtmlKit.Panel()
p2.title_('Detail Links')
p2t = HtmlKit.Table()
# expand group(s)
# use the inplace copy trick again
for ent in ents[:]:
if self.entityIsGroup(ent):
ents.remove(ent)
for ent in self.ConfigFile['Groups'][self.typename][string.split(ent, '_', 3)[2]]:
ents.append(ent)
for ent in ents:
(agent, entity) = string.split(ent, '_', 1)
nurl = HtmlKit.Url()
nurl.addPath_('downtime')
nurl.setArgument_toValue_('agent', agent)
nurl.setArgument_toValue_('entity', entity)
nurl.setArgument_toValue_('fromdate', vars['fromdate'])
nurl.setArgument_toValue_('fromtime', vars['fromtime'])
nurl.setArgument_toValue_('todate', vars['todate'])
nurl.setArgument_toValue_('totime', vars['totime'])
nurl.setArgument_toValue_('detail', 1)
if vars.has_key('reportparams'):
nurl.setArgument_toValue_('reportparams', 1)
if vars.has_key('summary'):
nurl.setArgument_toValue_('summary', 1)
if vars.has_key('graph'):
nurl.setArgument_toValue_('graph', 1)
if vars.has_key('asline'):
nurl.setArgument_toValue_('asline', 1)
if vars.has_key('xtime'):
nurl.setArgument_toValue_('xtime', 1)
p2tr = p2t.newRow().newColumn(HtmlKit.Center(HtmlKit.Link(nurl.unparsedUrl(), 'Downtime Report for %s' % ent)))
p2.content().append(p2t)
f.append(p2)
# finish up
request.push(str(p))
request.done()
def displayGraph(self, request, url):
vars = self.unquoteDict(url.argumentsDict())
if vars.has_key('ents'):
ents = cPickle.loads(urllib.unquote(vars['ents']))
ents.reverse()
else:
raise ValueError("Where's the beef?") # heh this is wrong
# Open the gnuplot pipe
gppath = self.ConfigFile['Qos'].get('gnuplotPath', '/usr/local/bin/gnuplot')
(gin, gout) = os.popen2(gppath)
if vars.has_key('xtime'):
xlabelstring = 'Date'
else:
xlabelstring = 'Time'
xtlrangedata = string.split(vars['fromtime'], ':')
xturangedata = string.split(vars['totime'], ':')
xdlrangedata = string.split(vars['fromdate'], '/')
xdurangedata = string.split(vars['todate'], '/')
xlrange = ('%s/%s %s:%s' % (xdlrangedata[0], xdlrangedata[1], xtlrangedata[0], xtlrangedata[1]))
xurange = ('%s/%s %s:%s' % (xdurangedata[0], xdurangedata[1], xturangedata[0], xturangedata[1]))
print "Ranges: %s %s" % (xlrange, xurange)
gin.write('set terminal png small color\n')
gin.write('set output\n')
gin.write('set size 1.3,0.5\n')
gin.write('set key below\n')
gin.write('set xdata time\n')
if vars.has_key('xtime'):
gin.write('set format x "%m/%d"\n')
else:
gin.write('set format x "%H:%M"\n')
gin.write('set timefmt "%m/%d %H:%M"\n')
gin.write('set xlabel "%s"\n' % xlabelstring)
gin.write('set noautoscale y\n')
gin.write('set nokey\n')
gin.write('set xrange ["%s":"%s"]\n' % (xlrange, xurange))
gin.write('set yrange [%s:%s]\n' % (-1, len(ents)+1))
yticlist = ['"Combined" 0']
for ent in ents:
yticlist.append('"%s" %s' % (ent, ents.index(ent) + 1))
gin.write('set ytics (%s)\n' % (string.join(yticlist, ', ')))
# need as many '- using 1:3's as there are entities to plot
plotapp = [ "plot '-' using 1:3 with linespoints pt 2" ]
for i in range(1, len(ents)):
plotapp.append("'-' using 1:3 with linespoints pt 2" )
# for cumulative data
plotapp.append("'-' using 1:3 with linespoints lt -1 pt 2")
plotline = string.join(plotapp, ', ')
print plotline
gin.write(plotline + '\n')
cumdata = []
for ent in ents:
cur = ents.index(ent) + 1
if self.entityIsGroup(ent):
(agent, entity, groupname) = string.split(ent, '_', 2)
groupents = self.ConfigFile['Groups'][self.typename][groupname]
groupdt = []
for gent in groupents:
(agent, entity) = string.split(gent, '_', 1)
dt = downtimer.Downtimer(agent=agent,
entity=entity,
startdate=vars['fromdate'],
starttime=vars['fromtime'],
enddate=vars['todate'],
endtime=vars['totime'])
groupdt.append(dt.run())
# now apply the glue
result = (dt.combine(groupdt))
else:
(agent, entity) = string.split(ent, '_', 1)
result = []
dt = downtimer.Downtimer(agent=agent,
entity=entity,
startdate=vars['fromdate'],
starttime=vars['fromtime'],
enddate=vars['todate'],
endtime=vars['totime'])
result = dt.run()
cumdata.append(result)
if len(result[0]) == 0:
#raise ValueError, "%s:Got no data to plot!" % ent
print "Entity %s had no downtime" % (ent)
gin.write('#\ne\n')
else:
for line in result[0]:
gin.write('%s %s\n' % (time.strftime('%m/%d %H:%M', time.localtime(line[0])), cur))
gin.write('%s %s\n' % (time.strftime('%m/%d %H:%M', time.localtime(line[0]+line[1])), cur))
gin.write('\n')
gin.write('e\n')
# Insert the cumulative data
udata = dt.combine(cumdata)
for line in udata[0]:
gin.write('%s %s\n' % (time.strftime('%m/%d %H:%M', time.localtime(line[0])), 0))
gin.write('%s %s\n' % (time.strftime('%m/%d %H:%M', time.localtime(line[0]+line[1])), 0))
gin.write('\n')
gin.write('e\n')
# finish up
gin.close()
request.push(gout.read())
request.done()