HAL9001-CheckMK/local/share/check_mk/agents/special/agent_hal9001

357 lines
12 KiB
Python

#!/usr/bin/env python3
import getopt
import sys
import random
import os
from time import (
time, localtime, strftime,
)
"""
Sample output
<<<hal9001_status:sep(59)>>>
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)
<<<hal9001_users:sep(59)>>>
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)
<<<hal9001_storages:sep(59)>>>
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)
}
}
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_<ul|dl>" 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("<<<hal9001_status:sep(59)>>>")
print(f"{status};{version};{username};{ipaddress}")
#@debugLog
def doCmkHalUsersOutput(users: dict, hostname: str) -> None:
print("<<<hal9001_users:sep(59)>>>")
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("<<<hal9001_storages:sep(59)>>>")
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()