#!/usr/bin/python3
import subprocess
import urllib.request
import urllib.error
import thriftpy
from thriftpy.rpc import make_client
from thriftpy.transport import TFramedTransportFactory
import shutil
import datetime
import os
import tftpy
import logging
# 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, tftp, thrift, ...)
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, service, state, host, 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 ' + hostname + ' ... ', end='')
response = subprocess.Popen(
['ping', '-c', '1', hostname],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
response = response.communicate()
error = response[1].decode('utf-8')
# Error happend
if error != '':
print('\033[91m' + 'error' + '\033[0m')
statusList.append(Status(name, hostname, 'error', 'Offline', 'ping', error.rstrip()))
else:
print('\033[92m' + 'success' + '\033[0m')
statusList.append(Status(name, hostname, 'success', 'Online', 'ping'))
logStatus(statusList[-1])
def https(name, url):
print('HTTPS request ' + url + ' ... ', end='')
try:
r = urllib.request.urlopen(url)
if r.getcode() == 200:
statusList.append(Status(name, url, 'success', 'Online', 'https'))
print('\033[92m' + 'success' + '\033[0m')
else:
statusList.append(Status(name, url, 'error', 'Offline', 'https'))
print('\033[91m' + 'error' + '\033[0m')
except urllib.error.URLError as e:
statusList.append(Status(name, url, 'error', 'Offline', 'https', msg=str(e)))
print('\033[91m' + 'error' + '\033[0m')
except ValueError:
statusList.append(Status(name, url, 'error', 'Offline', 'https', msg="Unknown url type"))
print('\033[91m' + 'error' + '\033[0m')
finally:
logStatus(statusList[-1])
def tftp(name, host, port, filename):
print('TFTP request ' + host + ' ...', end='')
hostname = host + ':' + str(port) + ':' + filename
try:
client = tftpy.TftpClient(host, port)
client.download(filename, 'tmp_tftp_file')
print('\033[92m' + 'success' + '\033[0m')
# Delete tmp tftp file
os.remove('tmp_tftp_file')
statusList.append(Status(name, hostname, 'success', 'Online', 'tftp', msg='File ' + filename + ' downloaded successfully.'))
except Exception as e:
print('\033[91m' + 'error' + '\033[0m')
statusList.append(Status(name, hostname, 'error', 'Offline', 'tftp', msg=str(e)))
finally:
logStatus(statusList[-1])
# Creates the thrift client and prwlp-pxe.ruf.uni-freiburg.de .ceeds the get Organisations call. Server can either be SAT or MASTER
def thrift(name, ip, port, server):
host = ip + ':' + str(port)
print('THRIFT request ' + host + ' ...', end='')
bwlp_thrift = thriftpy.load('bwlp.thrift', module_name='bwlp_thrift')
organisations = []
# Different clients for SAT / Master is needed.
try:
if server == 'SAT':
satserver = make_client(bwlp_thrift.SatelliteServer, ip, port, trans_factory=TFramedTransportFactory())
organisations = satserver.getAllOrganizations()
elif server == 'MASTER':
masterserver = make_client(bwlp_thrift.MasterServer, ip, port, trans_factory=TFramedTransportFactory())
organisations = masterserver.getOrganizations()
organisationList = []
for org in organisations:
organisationList.append(Organisation(org.organizationId, org.displayName))
if len(organisationList) == 0:
statusList.append(Status(name, host, 'warning', 'Online (' + str(len(organisationList)) + ')', 'thrift', msg=str(len(organisationList)) + " organizations found",data=organisationList))
else:
statusList.append(Status(name, host, 'success', 'Online (' + str(len(organisationList)) + ')', 'thrift', msg=str(len(organisationList)) + " organizations found",data=organisationList))
print('\033[92m' + 'success' + '\033[0m')
except ConnectionResetError:
statusList.append(Status(name, host, 'error', 'Offline', 'thrift', msg="ConnectionResetError: [Errno 104] Connection reset by peer"))
print('\033[91m' + 'error' + '\033[0m')
finally:
logStatus(statusList[-1])
# Parses the log from the logfile. Fills the logEntries array.
def parseLog():
global logEntries
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
log_string += '\t' + str(entry.msg)
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):
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.service, status.state, status.host, status.msg, status.data))
# Reads the config and executes the calls.
def readConfig():
if not (os.path.exists('bwlp.config')):
return
with open('bwlp.config', 'r') as config:
for line in config:
line = line.strip()
entry = line.split('\t')
if entry[0] == '#': continue
check(entry)
# Calls the check method.
def check(entry):
if entry[0] == 'ping':
if (len(entry) < 3): return
ping(entry[1], entry[2])
elif entry[0] == 'https':
if (len(entry) < 3): return
https(entry[1], entry[2])
elif entry[0] == 'thrift':
if (len(entry) < 5): return
thrift(entry[1], entry[2], int(entry[3]), entry[4])
elif entry[0] == 'tftp':
if (len(entry) < 5): return
tftp(entry[1], entry[2], int(entry[3]), entry[4])
# Parse the logfile.
parseLog()
# Call the checks.
readConfig()
# 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'
# Cut the log to the last n entries
n = 500
cut_amount = 0
if len(logEntries) >= n:
cut_amount = len(logEntries) - n
for entry in reversed(logEntries[cut_amount:]):
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 log_item_part_msg">'
+ 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()