#!/usr/bin/env python3 import getopt import sys import random import os from time import ( time, localtime, strftime, ) """ Sample output <<>> ok;SK-1st-Edition 1965-12-17;arthur;127.0.0.1 1. Status, possible values: ok, update available, storage down (String) 2. Version (String) 3. Username used for login (String) 4. IP used for access (String) <<>> user1;David Bowman;65536.0;262144.0 user2;Frank Poole;16384.0;131072.0 user3;Elena;32768.0;65536.0 1. User ID (string) 2. User Real Name (string) 3. Quota Used (float in bytes, absolut) 4. Quota Max (float in bytes, absolut) <<>> storage1;DATA_Dullea;8192.0;262144.0 storage2;DATA_Poole;2048.0;131072.0 1. Storage ID (string) 2. Storage Custom Name (string) 3. Upload (float in bytes, counter) 4. Download (float in bytes, counter) """ def showUsage(): sys.stderr.write("""HAL9001 Special Agent USAGE: agent_hal9001 -H [hostname] -u [username] -p [password] agent_hal9001 -h OPTIONS: -H, --hostname Hostname (FQDN or IP) of HAL System -u, --username Username -p, --password Password -h, --help Show this help message and exit """) debugLogFilename = "" SEP = "|" #SEP = "\t" TIMEFMT = "%Y-%m-%d %H:%M:%S" FLOATFMT = "{:.4f}" ''' Decorator function for debugging purposes creates a file with many information regarding the function call, like: timestamp name of function runtime number of arguments number of keyword arguments return value of function call type of return value all parameters given to function ''' def debugLog(function): def wrapper(*args, **kwargs): # execute function and measure runtime start = time() value = function(*args, **kwargs) end = time() # get number of args and kwargs len_args = len(args) len_kwargs = len(kwargs) # format the output seconds = FLOATFMT.format(end - start) local_time = strftime(TIMEFMT,localtime(start)) # get function name fname = function.__name__ # create output # out1: Timestamp|Name of Function|Runtime|Num Args|Num Kwargs|Return Value|Return Value Type out1 = f"{local_time}{SEP}{fname}{SEP}{seconds}{SEP}{len_args}{SEP}{len_kwargs}{SEP}{value}{SEP}{type(value)}" # out2: all arguments out2 = "" for arg in args: out2 = out2 + SEP + str(arg) # out 3: all keyword arguments out3 = "" if len_kwargs > 0: for key, val in kwargs.items(): out3 = out3 + SEP + key + ":" + str(val) # write output to file if debugLogFilename != "": try: with open(debugLogFilename, "a+") as f: f.write(f"{out1}{out2}{out3}\n") except: sys.stderr.write(f"Something went wrong when writing to file {debugLogFilename}\n") sys.exit(1) else: sys.stderr.write(f"Debug activated, but no debug filename given.\n") sys.exit(1) return value return wrapper # Parameters coming from CheckMK opt_hostname = "" opt_username = "" opt_password = "" # Base values for randomly calculating the use of the storage system per user base_usage = float(65536 * 8) # 0.5 MByte max_quota = 1073741824.0 # 1 GByte # these dicts are used as starting points, feel free to extend and/or adjust them # each user/storage results in an additional service users_dict = { "user1": { "realname": "David Bowman", "usage": base_usage*2, "quota_max": max_quota }, "user2": { "realname": "Frank Poole", "usage": base_usage*3, "quota_max": max_quota/2 }, "user3": { "realname": "Elena", "usage": base_usage*4, "quota_max": max_quota*2 } } storages_dict = { "storage1": { "realname": "DATA_Dullea", "download": float(1024**3), "upload": float((1024**2) * 6) }, "storage2": { "realname": "DATA_Poole", "download": float(1024**3), "upload": float((1024**2) * 4) }, "storage-tmp": { "realname": "DATA_TMP", "download": float(1024**2), "upload": float((1024**2) * 2) } } hal_status_list = ["ok", "update available", "storage down"] hal_version = "SK-1st-Edition 1965-12-17" short_options = 'hH:u:p:' long_options = [ 'hostname=', 'username=', 'password=', 'help' ] def getDebugFilename(hostname: str) -> str: home_path = os.getenv("HOME") tmp_path = f"{home_path}/tmp" file_name = f"{tmp_path}/hal9001_{hostname}_debug.log" return file_name def getOptions(): global opt_hostname global opt_username global opt_password opts, args = getopt.getopt(sys.argv[1:], short_options, long_options) for opt, arg in opts: if opt in ['-H', '--hostname']: opt_hostname = arg if opt in ['-u', '--username']: opt_username = arg elif opt in ['-p', '--password']: opt_password = arg elif opt in ['-h', '--help']: showUsage() sys.exit(0) def showOptions(): print(f"Hostname: {opt_hostname}") print(f"Username: {opt_username}") print(f"Password: {opt_password}") #@debugLog def calculateNewUserStorage(current_storage: float) -> float: # let the chance that no change occured be at 90% no_change = random.randint(1, 100) if no_change > 10: return float(current_storage) else: # create some variance, value of "amount" is interpreted in bytes factor = random.randint(1,4) amount = random.randint(1024000, 4096000) amount *= factor # increase or decrease, give increase a better chance (60:40), that's more real :-) plus_or_minus = random.randint(1,100) if plus_or_minus > 40: new_storage = current_storage + amount else: new_storage = current_storage - amount # avoid negative values if new_storage < 0: new_storage = 1024000 return float(new_storage) #@debugLog def calculateNewStorageCounters(ul_bytes: float, dl_bytes: float) -> tuple[float, float]: # let the chance that no change occured be at 2% no_change = random.randint(1, 100) if no_change > 98: return float(ul_bytes), float(dl_bytes) else: # create some variance, values of "amount_" is interpreted in bytes factor_ul = random.randint(1,7) amount_ul = random.randint(20240000, 50960000) amount_ul *= factor_ul factor_dl = random.randint(1,14) amount_dl = random.randint(30240000, 60960000) amount_dl *= factor_dl # we are simulating counters, so only increasing makes sense new_ul_bytes = ul_bytes + amount_ul new_dl_bytes = dl_bytes + amount_dl return float(new_ul_bytes), float(new_dl_bytes) #@debugLog def doCmkHalStatusOutput(status: str, version: str, username: str, ipaddress: str) -> None: print("<<>>") print(f"{status};{version};{username};{ipaddress}") #@debugLog def doCmkHalUsersOutput(users: dict, hostname: str) -> None: print("<<>>") home_path = os.getenv("HOME") tmp_path = f"{home_path}/tmp" for user in users: # we need a way to store the current value for storage usage between runs of the agent help_file = f"{tmp_path}/hal9001_{hostname}_{user}_current_storage.txt" if os.path.exists(help_file): # help file exists, so get the content with open(help_file, "r") as file: current_storage = float(file.read()) else: # help file does not exist, create it and store the start value in it with open(help_file, "w") as file: file.write(str(users[user]["usage"])) current_storage = users[user]["usage"] realname = users[user]["realname"] quota_max = users[user]["quota_max"] # simulate changes in storage usage new_storage = calculateNewUserStorage(current_storage) # save the new value in help file with open(help_file, "w") as file: file.write(str(new_storage)) # create output print(f"{user};{realname};{new_storage};{quota_max}") #@debugLog def doCmkHalStoragesOutput(storages: dict, hostname: str) -> None: print("<<>>") home_path = os.getenv("HOME") tmp_path = f"{home_path}/tmp" for storage in storages: # we need a way to store the current values for storage usage between runs of the agent # so we can simulate counters for uploaded/downloaded bytes help_file_ul = f"{tmp_path}/hal9001_{hostname}_{storage}_ul_bytes.txt" help_file_dl = f"{tmp_path}/hal9001_{hostname}_{storage}_dl_bytes.txt" if os.path.exists(help_file_ul): # help file exists, so get the content with open(help_file_ul, "r") as file: current_ul_bytes = float(file.read()) else: # help file does not exist, create it and store the start value in it with open(help_file_ul, "w") as file: file.write(str(storages[storage]["upload"])) current_ul_bytes = storages[storage]["upload"] if os.path.exists(help_file_dl): # help file exists, so get the content with open(help_file_dl, "r") as file: current_dl_bytes = float(file.read()) else: # help file does not exist, create it and store the start value in it with open(help_file_dl, "w") as file: file.write(str(storages[storage]["download"])) current_dl_bytes = storages[storage]["download"] realname = storages[storage]["realname"] # simulate changes in storage usage new_ul_bytes, new_dl_bytes = calculateNewStorageCounters(current_ul_bytes, current_dl_bytes) # save the new values in help files with open(help_file_ul, "w") as file: file.write(str(new_ul_bytes)) with open(help_file_dl, "w") as file: file.write(str(new_dl_bytes)) # create output print(f"{storage};{realname};{new_ul_bytes};{new_dl_bytes}") #@debugLog def getStatus() -> tuple[str, str]: # randomly set one of the three available states status_index = random.randint(1,100) if status_index <= 5: # should result in critical state hal_status = hal_status_list[2] elif status_index <= 15: # should result in warning state hal_status = hal_status_list[1] else: # should result in ok state hal_status = hal_status_list[0] return hal_status, hal_version #@debugLog def doLogin(hostname: str, username: str, password: str) -> tuple[bool, int]: # simulate the login to our HAL system # give it a chance of 2% to fail to demonstrate an error from time to time success = random.randint(1, 100) if success > 2: return True, 200 else: return False, 401 def main(): global debugLogFilename getOptions() #showOptions() # some checks # keep in mind: the 1st line printed out to std.err will be shown as check result in CheckMK if (opt_hostname == ""): sys.stderr.write(f"No hostname given.\n") showUsage() sys.exit(1) if (opt_username == ""): sys.stderr.write(f"No username given.\n") showUsage() sys.exit(1) if (opt_password == ""): sys.stderr.write(f"No password given.\n") showUsage() sys.exit(1) debugLogFilename = getDebugFilename(opt_hostname) success, code = doLogin(opt_hostname, opt_username, opt_password) if success: status, version = getStatus() doCmkHalStatusOutput(status, version, opt_username, opt_hostname) doCmkHalUsersOutput(users_dict, opt_hostname) doCmkHalStoragesOutput(storages_dict, opt_hostname) else: sys.stderr.write(f"Login to {opt_hostname} with user {opt_username} failed with error code {code}\n") sys.exit(1) if __name__ == "__main__": main()