(root)/available.py - Rev 1
Go to most recent revision |
Blame |
Compare with Previous |
Last modification |
View Log
| RSS feed
#!/usr/bin/python
# -*- coding: utf-8 -*-
# File: available.py
# Author: Tomás Vírseda
# License: The code is licensed under the terms of the GPL v3
# so you're free to grab, extend, improve and fork the code
# as you want
# Description: availability overview of an SAP System
import os
import sys
from datetime import timedelta
from pprint import pprint
from datetime import datetime
class Available:
def __init__(self, logfile, sapsid='SAP', maxdown=10):
self.data = {}
self.stats = {}
self.props = {}
self.props['FILENAME_LOG'] = logfile
self.props['SHOW_MAX_DOWNTIMES'] = maxdown
self.stats['SAPSID'] = sapsid
self.__get_contents()
self.__group_status()
def __get_contents(self):
"""
Convert from available.log raw data to structured data
"""
LOGFILE = self.props['FILENAME_LOG']
if not os.path.exists(LOGFILE):
sys.exit('wrong path to available.log file')
try:
flog = open(LOGFILE, 'r')
except:
exit('Error opening log file. Check permissions.')
self.stats['RAW'] = flog.read()
flog.seek(0) # Back to beguining of file
lines = flog.readlines()
serie = []
for line in lines:
detail = line.split('\n')[0]
if detail.split(' ')[0] == 'Unavailable':
availability, ds, ts, sep, de, te = detail.split(' ')
availability = False
else:
availability, sep, sep, ds, ts, sep, de, te = detail.split(' ')
availability = True
sdtraw = ds + " " + ts
edtraw = de + " " + te
dstart = datetime.strptime(sdtraw, "%d.%m.%Y %H:%M:%S")
dend = datetime.strptime(edtraw, "%d.%m.%Y %H:%M:%S")
registry = [availability, dstart, dend]
serie.append(registry)
flog.close()
self.stats['SERIE'] = serie
def __group_status(self):
"""
Group consecutive availability status
"""
final = []
n = 0
for cur_entry in self.stats['SERIE']:
if n != 0:
# Add consecutives entries but, if status remains the
# same, all entries must be grouped keeping the first
# entry's start date.
prv_entry = self.stats['SERIE'][n - 1]
prv_status, prv_start, prv_end = prv_entry
cur_status, cur_start, cur_end = cur_entry
same_status = cur_status == prv_status
if same_status:
# save start time of group status before delete
prv_start = final[len(final)-1][1]
del final[len(final)-1]
new_entry = [cur_status, prv_start, cur_end]
final.append(new_entry)
else:
# Add first entry
final.append(cur_entry)
else:
final.append(cur_entry)
n = n + 1
self.stats['FINAL'] = final
def get_stats(self):
return self.stats
def get_data(self):
return self.data
def get_properties(self):
return self.props
def build_report(self):
"""
Build SAP System Report from available.log contents.
"""
availability = {}
availability[False] = {}
availability[True] = {}
REPORT = ""
REPORT += "Report: SAP System '%s' Availability Stats\n" % self.stats['SAPSID']
REPORT += "Source: %s\n" % self.props['FILENAME_LOG']
REPORT += "-------------------------------------------\n\n"
final = self.stats['FINAL']
self.stats['START'] = start = final[0][1]
self.stats['END'] = end = final[-1][2]
self.stats['DELTA'] = delta = end - start
self.stats['IS_AVAILABLE'] = is_available = final[-1][0]
self.stats['AVAILABLE'] = {}
self.stats['AVAILABLE'][True] = {}
self.stats['AVAILABLE'][False] = {}
for entry in final:
is_available = entry[0]
dstart = entry[1]
dend = entry[2]
edelta = dend - dstart
etotsec = int(edelta.total_seconds())
availability[is_available]['last'] = dend
try:
# Number of seconds in this status
seconds = availability[is_available]['seconds']
seconds = seconds + etotsec
availability[is_available]['seconds'] = seconds
self.stats['AVAILABLE'][is_available]['SECONDS'] = seconds
# Update count
count = availability[is_available]['count']
count = count + 1
availability[is_available]['count'] = count
self.stats['AVAILABLE'][is_available]['COUNT'] = count
except:
availability[is_available]['seconds'] = etotsec
availability[is_available]['count'] = 1
self.stats['AVAILABLE'][is_available]['SECONDS'] = etotsec
self.stats['AVAILABLE'][is_available]['COUNT'] = 1
try:
# Register entry
registry = availability[is_available]['registry']
registry.append(entry)
availability[is_available]['registry'] = registry
self.stats['AVAILABLE'][is_available]['REGISTRY'] = registry
except:
availability[is_available]['registry'] = []
self.stats['AVAILABLE'][is_available]['REGISTRY'] = []
self.stats['LAST_UP'] = lastup = availability[True]['last']
self.stats['DOWN_COUNT'] = numdowntime = availability[False]['count']
self.stats['DOWN_SECONDS'] = totdowntime = availability[False]['seconds']
self.stats['UP_COUNT'] = numuptime = availability[True]['count']
self.stats['UP_SECONDS'] = totuptime = availability[True]['seconds']
self.stats['DOWN_PERCENTAJE'] = ptotdowntime = totdowntime * 100 / delta.total_seconds()
self.stats['UP_PERCENTAJE'] = ptotuptime = totuptime * 100 / delta.total_seconds()
self.stats['DOWN_LIST'] = downtimes = availability[False]['registry']
downtimes.reverse()
REPORT += "First entry recorded: %s\n" % start
REPORT += " Last entry recorded: %s\n" % end
REPORT += " Is Available: %s\n" % is_available
if not is_available:
REPORT += " Last time up: %s\n" % lastup
REPORT += " SAP Lifetime: %s\n" % delta
REPORT += " Unavailable: %s, %d times down, %.02f%% of lifetime\n" % (str(timedelta(seconds=availability[False]['seconds'])), numdowntime, ptotdowntime)
REPORT += " Available: %s, %.02f%% of lifetime\n\n" % (str(timedelta(seconds=availability[True]['seconds'])), ptotuptime)
REPORT += "Last %d/%d downtimes:\n" % (self.props['SHOW_MAX_DOWNTIMES'], len(downtimes))
for n in range(len(downtimes)):
if n < self.props['SHOW_MAX_DOWNTIMES']:
start = downtimes[n][1]
end = downtimes[n][2]
delta = end - start
duration = str(timedelta(seconds=delta.total_seconds()))
REPORT += "\t%2d - Down from: %s\t\tto: %s\tDuration: %s\n" % (n + 1, downtimes[n][1], downtimes[n][2], duration)
REPORT += ""
REPORT += "\nReport built on %s\n" % datetime.now()
self.stats['REPORT_TXT'] = REPORT
return REPORT
#~ def debug(self):
#~ print ("")
#~ print ("Debug stats:")
#~ mytottime = int(availability[True]['seconds'] + availability[False]['seconds'])
#~ saptottime = int(delta.total_seconds())
#~ print (" My total time: %s" % str(timedelta(seconds=mytottime)))
#~ print (" SAP total time: %s" % str(timedelta(seconds=saptottime)))
#~ print (" Gaps: %s" % str(timedelta(seconds=(int(saptottime - mytottime)))))
if __name__ == "__main__":
try:
av = Available(logfile='available.log', sapsid='DBT', maxdown=15)
#~ av.set_sapsid('DEV')
#~ serie = av.get_contents()
#~ av.group_status(serie)
print(av.build_report())
except Exception as error:
raise
msg = "Error. You must provide a path to available.log\n"
msg += "Eg.: python3 available.py /usr/sap/SID/DVEBMGS00/work/available.log"
sys.exit (msg)
# Build chart
#~ import pygal
#~ dateline = pygal.DateTimeLine(x_label_rotation=35, truncate_label=-1)
#~ status1 = []
#~ status2 = []
#~ for entry in final:
#~ available = entry[0]
#~ dstart = entry[1]
#~ dend = entry[2]
#~ delta = dend - dstart
#~ if not available:
#~ status1.append((dstart, 0))
#~ status1.append((dend, 0))
#~ else:
#~ status2.append((dstart, 1))
#~ status2.append((dend, 1))
#~ dateline.add("Offline", status1)
#~ dateline.add("Online", status2)
#~ fout = open('chart.svg', 'wb')
#~ fout.write(dateline.render())
#~ fout.close()