#!/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 += ('