diff options
-rw-r--r-- | bwLpStatus.py | 212 | ||||
-rw-r--r-- | bwlpMonitor_template.html | 39 | ||||
-rw-r--r-- | img/favicon.ico | bin | 0 -> 452 bytes | |||
-rw-r--r-- | img/logo_bwlehrpool.png | bin | 0 -> 14298 bytes | |||
-rw-r--r-- | img/wiki_bwlehrpool.png | bin | 0 -> 741 bytes | |||
-rw-r--r-- | style.css | 196 |
6 files changed, 447 insertions, 0 deletions
diff --git a/bwLpStatus.py b/bwLpStatus.py new file mode 100644 index 0000000..a0642a8 --- /dev/null +++ b/bwLpStatus.py @@ -0,0 +1,212 @@ +import subprocess +import urllib.request +import urllib.error +#import mysql.connector +import thriftpy +from thriftpy.rpc import make_client +from thriftpy.transport import TFramedTransportFactory +import shutil +import datetime +import os + +# Global variables +statusList = [] +logEntries = [] +newLogIndex = 0 + +# Class of the status objects +class Status: + def __init__(self, name, host, state, status, service, msg = '', data = []): + # Name of the service. + self.name = name + + # Hostname (Domain, ip, ...) + self.host = host + + # State of the response. (success, warning, error) <-- This class is responsible for the coloring (green, orange, red) + self.state = state + + # Status of the check. (Online, Offline, Temporaily not available, Some services unavailable, ...) <-- This text is shown on the website + self.status = status + + # Type of the service. (ping, https, http, mysql, ...) + self.service = service + + # Message e.g. error message + self.msg = msg + + # Data the request return e.g. organisation list from the thrift client. + self.data = data + +class LogEntry: + def __init__(self, date, host, service, state, msg = '', data = []): + self.date = date + self.host = host + self.service = service + self.state = state + self.msg = msg + self.data = data + +class Organisation: + def __init__(self, id, name): + self.id = id + self.name = name + + def __repr__(self): + return str(self.__dict__) + +# Check connection functions. +def ping(name, hostname): + # Ping a hostname and tell if the server is up or down. + print('Ping request ... ', end='') + response = subprocess.Popen( + ['ping', '-c', '1', hostname], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ) + response = response.communicate() + output = response[0].decode('utf-8') + error = response[1].decode('utf-8') + + # Error happend + if error != '': + print('error: ', end='') + print(error) + statusList.append(Status(name, hostname, 'error', 'Offline', 'ping', error.rstrip())) + else: + print('success') + statusList.append(Status(name, hostname, 'success', 'Online', 'ping')) + + logStatus(statusList[-1]) + +def https(name, url): + print('HTTPS request') + try: + r = urllib.request.urlopen(url) + if r.getcode() == 200: + statusList.append(Status(name, url, 'success', 'Online', 'https')) + else: + statusList.append(Status(name, url, 'error', 'Offline', 'https')) + + except urllib.error.URLError as e: + statusList.append(Status(name, url, 'error', 'Offline', 'https')) + + logStatus(statusList[-1]) + +def mysql(self, host, user, passwd, db): + print('MYSQL request start ...') + #db = mysql.connector.connect( + # host='localhost', + # user='openslx', + # passwd='geheim', + # database='openslx' + #) + #cursor = db.cursor() + #cursor.execute('SELECT * FROM user') + #result = cursor.fetchall() + #for user in result: + # print(user[1]) + +def thrift(name, ip, port): + print('THRIFT request start ...') + bwlp_thrift = thriftpy.load('bwlp.thrift', module_name='bwlp_thrift') + # masterserver = make_client(bwlp_thrift.MasterServer, '132.230.4.16', 9090, trans_factory=TFramedTransportFactory()) + # TODO: TRY CATCH + satserver = make_client(bwlp_thrift.SatelliteServer, ip, port, trans_factory=TFramedTransportFactory()) + organisations = satserver.getAllOrganizations() + organisationList = [] + for org in organisations: + organisationList.append(Organisation(org.organizationId, org.displayName)) + + host = ip + ':' + str(port) + statusList.append(Status(name, host, 'success', 'Online (' + str(len(organisationList)) + ')', 'thrift', data=organisationList)) + logStatus(statusList[-1]) + +# Parses the log from the logfile. Fills the logEntries array. +def parseLog(): + if not (os.path.exists('bwLpStatusLog.txt')): + return + with open('bwLpStatusLog.txt', 'r') as log: + for line in log: + line = line.strip() + entry = line.split('\t') + if len(entry) < 4: continue + logEntries.append(LogEntry(entry[0], entry[1], entry[2], entry[3], '' if len(entry) < 5 else entry[4], [] if len(entry) < 6 else entry[5])) + global newLogIndex + newLogIndex = len(logEntries) + +# Writes updated log in the logfile. +def writeLog(): + with open('bwLpStatusLog.txt', 'a') as log: + for entry in logEntries[newLogIndex:]: + log_string = entry.date + '\t' + entry.service + '\t' + entry.state + '\t' + entry.host + if entry.msg != '': log_string += '\t' + entry.msg + if entry.data != []: log_string += '\t' + str(entry.data) + log.write(log_string + '\n') + +# Returns the most recent log object to a given status. None if there is none. +def getLogEntry(status): + # for entry in reversed(logEntries): + # if status.host == entry.host and status.service == entry.service: return entry + return next((x for x in reversed(logEntries) if (x.host == status.host) and (x.service == status.service)), None) + +# Checks weather the status has to be logged or not. (Does the status has changes from the last time?) +def logStatus(status): + obj = getLogEntry(status) + if (obj is None) or (status.state != obj.state): + date = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + logEntries.append(LogEntry(date, status.host, status.service, status.state, status.msg, status.data)) + +# Parse the logfile. +parseLog() + +# Call the checks. +https('Masterserver HTTPS Service', 'https://bwlp-masterserver.ruf.uni-freiburg.de') +ping('Masterserver IP Ping', '132.230.4.16') +ping('Fileserver Ping', 'files.bwlp.ks.uni-freiburg.de') +ping('Backup fileserver Ping', 'bwlp-backup.ruf.uni-freiburg.de') +thrift('Thrift SAT', '132.230.8.192', 9090) +ping('BAS Ping', 'bas.intra.uni-freiburg.de') +ping('yc', '127.0.0.2') +ping('yx', '127.0.0.3') + +# Write the new logfile. +writeLog() + +# HTML Processing to generate the website. +code = '\r\n' +for status in statusList: + # Prepare and calculate the time since the server is online / offline + obj = getLogEntry(status) + now = datetime.datetime.now() + date = datetime.datetime.strptime(obj.date, '%Y-%m-%d %H:%M:%S') + time = now - date + days = time.days + hours = time.seconds // 3600 + minutes = (time.seconds // 60) % 60 + timeString = '' + if days > 0: + timeString += str(days) + ' days ' + if hours > 0: + timeString += str(hours) + ' hours ' + if minutes > 0: + timeString += str(minutes) + ' minutes' + + code += '<div class="content_item"><div class="content_item_part"><div class="content_item_part_title">' + status.name + '</div><div class="content_item_part_subtitle">' + status.host + '</div></div><div class="content_item_part ' + status.state + '"><div class="content_item_part_title">' + status.status + '</div><div class="content_item_part_subtitle">' + timeString + '</div></div></div>\r\n' + +# Generate the html code for the log. +log = '\r\n' +for entry in reversed(logEntries): + log += '<div class="log_item"><div class="log_item_part time"><div class="content_item_part_title">[' + entry.date + ']</div></div><div class="log_item_part service"><div class="content_item_part_title">[' + entry.service + ']</div></div><div class="log_item_part ' + entry.state + '"><div class="content_item_part_title">[' + entry.state + ']</div></div><div class="log_item_part"><div class="content_item_part_title">[' + entry.host +']</div></div><div class="log_item_part"><div class="content_item_part_title">' + entry.msg + '</div></div></div>' + +# Copy the .html file and replace the %CONTENT% to generate the final html file. +shutil.copyfile('bwlpMonitor_template.html', 'bwlpMonitor.html') + +# Replace the %CONTENT% in the template with the actual html code. +html = open('bwlpMonitor_template.html') +html2 = open('bwlpMonitor.html', 'w') +for line in html: + html2.write(line.replace('%CONTENT%', code).replace('%LOG%', log)) + +html.close() +html2.close() diff --git a/bwlpMonitor_template.html b/bwlpMonitor_template.html new file mode 100644 index 0000000..e90b251 --- /dev/null +++ b/bwlpMonitor_template.html @@ -0,0 +1,39 @@ +<!DOCTYPE html> +<html> + <head> + <link rel="stylesheet" type="text/css" href="style.css"> + <link rel="icon" type="image/x-icon" href="img/favicon.ico"> + <title>bwLp Status Monitor</title> + </head> + <body> + <div id="header"> + <div id="header_left"> + <a href=""> + <img id="header_image" src="img/logo_bwlehrpool.png" alt="bwLehrpool"> + </a> + </div> + <div id="header_center"> + <label id="header_label">Status Monitor</label> + </div> + <div id="header_right"> + <div> + <a href="https://www.bwlehrpool.de/doku.php/start"> + <img id="header_wiki" src="img/wiki_bwlehrpool.png" alt="bwLehrpool"> + </a> + </div> + <a id="header_wiki_label" href="https://www.bwlehrpool.de/doku.php/start">bwlp Wiki</a> + </div> + </div> + <div id="body"> + <div id="content"> +%CONTENT% + </div> + + <div id="log"> +%LOG% + </div> + </div> + + </body> + +</html> diff --git a/img/favicon.ico b/img/favicon.ico Binary files differnew file mode 100644 index 0000000..f9a714e --- /dev/null +++ b/img/favicon.ico diff --git a/img/logo_bwlehrpool.png b/img/logo_bwlehrpool.png Binary files differnew file mode 100644 index 0000000..b8661a7 --- /dev/null +++ b/img/logo_bwlehrpool.png diff --git a/img/wiki_bwlehrpool.png b/img/wiki_bwlehrpool.png Binary files differnew file mode 100644 index 0000000..2a00915 --- /dev/null +++ b/img/wiki_bwlehrpool.png diff --git a/style.css b/style.css new file mode 100644 index 0000000..eaba8a2 --- /dev/null +++ b/style.css @@ -0,0 +1,196 @@ +html { + background: #ffffff; + font: normal 100%/1.4 sans-serif; + margin: unset; + height: 100%; +} + +body { + display: flex; + flex-direction: column; + align-items: center; + justify-content: flex-start; + margin: unset; +} + +a { + color: #137ba9; + text-decoration: none; +} + +a:hover { + color: #014c8c; + !text-decoration: underline; +} + +#header { + display: flex; + flex-direction: row; + width: 100%; + justify-content: space-between; + white-space: nowrap; + background-color: white; + height: 60px; + position: fixed; + box-shadow: 0 2px 4px 0 rgba(0,0,0,0.16),0 2px 10px 0 rgba(0,0,0,0.12) !important; +} + +#header_left { + display: flex; + flex-direction: row; + justify-content: left; + align-items: center; + width: 228px; +} + +#header_center { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + margin-right: 16px; + margin-left: 16px; +} + +#header_right { + display: flex; + flex-direction: row; + justify-content: right; + align-items: center; + margin-right: 8px; + width: 228px; +} + +#header_label { + color: #949494; + font-size: 50px; +} + +#header_image { + width: auto; + height: 60px; + margin-top: 8px; + margin-left: 8px; +} + +#header_wiki_label { + display: flex; + align-items: center; + font-size: 24px; + margin-left: 8px; + height: 100%; +} + +#body { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + margin-top: 90px; + min-height: 300px; +} + +#content { + display: flex; + flex-direction: column; + width: 800px; + box-shadow: 0 2px 4px 0 rgba(0,0,0,0.16),0 2px 10px 0 rgba(0,0,0,0.12) !important; + background-color: #f5f5f5; + overflow: auto; + margin-top: 30px; + min-height: 150px; + flex-shrink: 1; +} + +.content_item { + display: flex; + flex-direction: row; + justify-content: center; + justify-items: stretch; + align-items: center; + min-height: 50px; + height: 80px; + border-bottom: 1px solid #e2e2e2; +} + +.content_item:hover { + background-color: #efeeee; +} + +.content_item_part { + height: 100%; + width: 100%; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + border-left: 1px solid #e2e2e2; + border-right: 1px solid #e2e2e2; +} + +.content_item_part_title { +} + +.content_item_part_subtitle { + color: #000; + font-size: 10px; +} + +.success { + color: #58dc42; !#15e005; +} + +.warning { + color: #f6cc5e; +} + +.error { + color: #ce2323; +} + +#log { + display: flex; + flex-direction: column; + box-shadow: 0 2px 4px 0 rgba(0,0,0,0.16),0 2px 10px 0 rgba(0,0,0,0.12) !important; + background-color: #f5f5f5; + min-height: 150px; + max-height: 600px; + white-space: nowrap; + overflow: auto; + margin-top: 60px; + flex-shrink: 5; + margin-bottom: 90px; +} + +.log_item { + display: flex; + flex-direction: row; + justify-content: flex-start; + justify-items: stretch; + align-items: center; + border-bottom: 1px solid #e2e2e2; + min-height: 34px; + width: 100%; +} + +.log_item_part { + height: 100%; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + margin-right: 5px; +} + +.time { + color: #40bac8; + padding-left: 10px; +} + +.service { + color: #2196f3; +} + +.log_item:hover { + background-color: #efeeee; +} |