350 lines
12 KiB
Python
Executable File
350 lines
12 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# pylint: disable=pointless-string-statement
|
|
"""HAL9002 special agent"""
|
|
|
|
import sys
|
|
import os
|
|
import getopt
|
|
import random
|
|
|
|
"""
|
|
Sample output
|
|
|
|
<<<hal9002_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)
|
|
5. Desired state, id an update is available (String)
|
|
|
|
<<<hal9002_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)
|
|
|
|
<<<hal9002_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 show_usage() -> None:
|
|
"""shows help for usage"""
|
|
sys.stderr.write(
|
|
"""HAL9002 Special Agent
|
|
USAGE: agent_hal9002 -H [hostname] -u [username] -p [password]
|
|
agent_hal9002 -h
|
|
OPTIONS:
|
|
-H, --hostname Hostname (FQDN or IP) of HAL System
|
|
-u, --username Username
|
|
-p, --password Password
|
|
-s, --state-if-update-is-available State of service if an update is available (0/1/2/3)
|
|
-h, --help Show this help message and exit
|
|
"""
|
|
)
|
|
|
|
|
|
SEP: str = "|"
|
|
TIMEFMT: str = "%Y-%m-%d %H:%M:%S"
|
|
FLOATFMT: str = "{:.4f}"
|
|
|
|
# Parameters coming from CheckMK
|
|
opt_hostname: str = ""
|
|
opt_username: str = ""
|
|
opt_password: str = ""
|
|
opt_state_if_update_is_available: str = ""
|
|
|
|
# Base values for randomly calculating the use of the storage system per user
|
|
base_usage: float = float(65536 * 8) # 0.5 MByte
|
|
max_quota: float = 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: dict[str, str] = {
|
|
"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: dict[str, str] = {
|
|
"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: list[str] = ["ok", "update available", "storage down"]
|
|
hal_version: str = "SK-1st-Edition 1965-12-17"
|
|
|
|
short_options: str = "hH:u:p:s:"
|
|
long_options: list[str] = [
|
|
"hostname=",
|
|
"username=",
|
|
"password=",
|
|
"state-if-update-is-available=",
|
|
"help",
|
|
]
|
|
|
|
|
|
def get_options() -> None:
|
|
"""retrieves all command line options"""
|
|
global opt_hostname
|
|
global opt_username
|
|
global opt_password
|
|
global opt_state_if_update_is_available
|
|
|
|
opts, _ = 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 ["-s", "--state-if-update-is-available"]:
|
|
opt_state_if_update_is_available = arg
|
|
elif opt in ["-h", "--help"]:
|
|
show_usage()
|
|
sys.exit(0)
|
|
|
|
|
|
def show_options() -> None:
|
|
"""show the options from the command line"""
|
|
print(f"Hostname: {opt_hostname}")
|
|
print(f"Username: {opt_username}")
|
|
print(f"Password: {opt_password}")
|
|
print(f"State if update is available: {opt_state_if_update_is_available}")
|
|
|
|
|
|
def calculate_new_user_storage(current_storage: float) -> float:
|
|
"""calculates new values for the user's storage"""
|
|
# let the chance that no change occured be at 90%
|
|
no_change: int = random.randint(1, 100)
|
|
if no_change > 10:
|
|
return float(current_storage)
|
|
else:
|
|
# create some variance, value of "amount" is interpreted in bytes
|
|
factor: int = random.randint(1, 4)
|
|
amount: int = random.randint(1024000, 4096000)
|
|
amount *= factor
|
|
# increase or decrease, give increase a better chance (60:40), that's more real :-)
|
|
plus_or_minus: int = random.randint(1, 100)
|
|
if plus_or_minus > 40:
|
|
new_storage: int = current_storage + amount
|
|
else:
|
|
new_storage: int = current_storage - amount
|
|
# avoid negative values
|
|
if new_storage < 0:
|
|
new_storage = 1024000
|
|
return float(new_storage)
|
|
|
|
|
|
def calculate_new_storage_counters(
|
|
ul_bytes: float, dl_bytes: float
|
|
) -> tuple[float, float]:
|
|
"""calculates new values for the user's storage counters"""
|
|
# let the chance that no change occured be at 2%
|
|
no_change: int = random.randint(1, 100)
|
|
if no_change > 98:
|
|
return float(ul_bytes), float(dl_bytes)
|
|
# create some variance, values of "amount_<ul|dl>" is interpreted in bytes
|
|
factor_ul: int = random.randint(1, 7)
|
|
amount_ul: int = random.randint(20240000, 50960000)
|
|
amount_ul *= factor_ul
|
|
factor_dl: int = random.randint(1, 14)
|
|
amount_dl: int = random.randint(30240000, 60960000)
|
|
amount_dl *= factor_dl
|
|
# we are simulating counters, so only increasing makes sense
|
|
new_ul_bytes: int = ul_bytes + amount_ul
|
|
new_dl_bytes: int = dl_bytes + amount_dl
|
|
return float(new_ul_bytes), float(new_dl_bytes)
|
|
|
|
|
|
def do_cmk_hal_status_output(
|
|
status: str,
|
|
version: str,
|
|
username: str,
|
|
ipaddress: str,
|
|
state_if_update_is_available: str,
|
|
) -> None:
|
|
"""prints out the section for status"""
|
|
print("<<<hal9002_status:sep(59)>>>")
|
|
print(f"{status};{version};{username};{ipaddress};{state_if_update_is_available}")
|
|
|
|
|
|
def do_cmk_hal_users_output(users: dict, hostname: str) -> None:
|
|
"""prints out the section for users"""
|
|
print("<<<hal9002_users:sep(59)>>>")
|
|
current_storage: float = 0.0
|
|
home_path: str = os.getenv("HOME")
|
|
tmp_path: str = 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: str = f"{tmp_path}/hal9002_{hostname}_{user}_current_storage.txt"
|
|
if os.path.exists(help_file):
|
|
# help file exists, so get the content
|
|
with open(help_file, "r", encoding="utf-8") 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", encoding="utf-8") as file:
|
|
file.write(str(users[user]["usage"]))
|
|
current_storage = users[user]["usage"]
|
|
realname: str = users[user]["realname"]
|
|
quota_max: str = users[user]["quota_max"]
|
|
# simulate changes in storage usage
|
|
new_storage: float = calculate_new_user_storage(current_storage)
|
|
# save the new value in help file
|
|
with open(help_file, "w", encoding="utf-8") as file:
|
|
file.write(str(new_storage))
|
|
# create output
|
|
print(f"{user};{realname};{new_storage};{quota_max}")
|
|
|
|
|
|
def do_cmk_hal_storages_output(storages: dict, hostname: str) -> None:
|
|
"""prints out the section for storages"""
|
|
print("<<<hal9002_storages:sep(59)>>>")
|
|
current_dl_bytes: float = 0.0
|
|
current_ul_bytes: float = 0.0
|
|
new_dl_bytes: float = 0.0
|
|
new_ul_bytes: float = 0.0
|
|
home_path: str = os.getenv("HOME")
|
|
tmp_path: str = 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: str = f"{tmp_path}/hal9002_{hostname}_{storage}_ul_bytes.txt"
|
|
help_file_dl: str = f"{tmp_path}/hal9002_{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", encoding="utf-8") 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", encoding="utf-8") 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", encoding="utf-8") 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", encoding="utf-8") as file:
|
|
file.write(str(storages[storage]["download"]))
|
|
current_dl_bytes = storages[storage]["download"]
|
|
realname: str = storages[storage]["realname"]
|
|
# simulate changes in storage usage
|
|
new_ul_bytes, new_dl_bytes = calculate_new_storage_counters(
|
|
current_ul_bytes, current_dl_bytes
|
|
)
|
|
# save the new values in help files
|
|
with open(help_file_ul, "w", encoding="utf-8") as file:
|
|
file.write(str(new_ul_bytes))
|
|
with open(help_file_dl, "w", encoding="utf-8") as file:
|
|
file.write(str(new_dl_bytes))
|
|
# create output
|
|
print(f"{storage};{realname};{new_ul_bytes};{new_dl_bytes}")
|
|
|
|
|
|
def get_status() -> tuple[str, str]:
|
|
"""returns status and version"""
|
|
# randomly set one of the three available states
|
|
hal_status: str = ""
|
|
status_index: int = 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
|
|
|
|
|
|
def do_login(_hostname: str, _username: str, _password: str) -> tuple[bool, int]:
|
|
"""returns whetjer login was successful or not and a status code"""
|
|
# simulate the login to our HAL system
|
|
# give it a chance of 2% to fail to demonstrate an error from time to time
|
|
success: int = random.randint(1, 100)
|
|
if success > 2:
|
|
return True, 200
|
|
return False, 401
|
|
|
|
|
|
def main():
|
|
"""main function"""
|
|
success: bool = False
|
|
code: int = -1
|
|
status: str = "no status"
|
|
version: str = "no version"
|
|
get_options()
|
|
# show_options()
|
|
# 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("No hostname given.\n")
|
|
show_usage()
|
|
sys.exit(1)
|
|
if opt_username == "":
|
|
sys.stderr.write("No username given.\n")
|
|
show_usage()
|
|
sys.exit(1)
|
|
if opt_password == "":
|
|
sys.stderr.write("No password given.\n")
|
|
show_usage()
|
|
sys.exit(1)
|
|
# if opt_state_if_update_is_available == "":
|
|
# sys.stderr.write("No state given when update is available.\n")
|
|
# show_usage()
|
|
# sys.exit(1)
|
|
success, code = do_login(opt_hostname, opt_username, opt_password)
|
|
if success:
|
|
status, version = get_status()
|
|
do_cmk_hal_status_output(
|
|
status,
|
|
version,
|
|
opt_username,
|
|
opt_hostname,
|
|
opt_state_if_update_is_available,
|
|
)
|
|
do_cmk_hal_users_output(users_dict, opt_hostname)
|
|
do_cmk_hal_storages_output(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()
|