diff options
| author | user <user@node5.net> | 2024-01-02 05:16:27 +0100 |
|---|---|---|
| committer | user <user@node5.net> | 2024-01-02 05:16:27 +0100 |
| commit | 937a5571a158115060f062b066543132d8c66752 (patch) | |
| tree | 9ade99f4118dab905197d467b82796beaf44b6c8 /src | |
initial commit. Ping and http status works + state change
Diffstat (limited to 'src')
| -rwxr-xr-x | src/check_server_status.sh | 2 | ||||
| -rw-r--r-- | src/config.template.yml | 29 | ||||
| -rw-r--r-- | src/server_status.py | 164 |
3 files changed, 195 insertions, 0 deletions
diff --git a/src/check_server_status.sh b/src/check_server_status.sh new file mode 100755 index 0000000..4edbb8d --- /dev/null +++ b/src/check_server_status.sh @@ -0,0 +1,2 @@ +#!/bin/bash +echo "test" | socat - UNIX-CONNECT:/tmp/server_status.sock diff --git a/src/config.template.yml b/src/config.template.yml new file mode 100644 index 0000000..dbb75da --- /dev/null +++ b/src/config.template.yml @@ -0,0 +1,29 @@ +telegram: + token: "" + chat_id: +hosts: + node5.net: + urls: + - https://node5.net + - https://blog.node5.net/ + - https://git.node5.net/ + - https://ip.node5.net/ + - https://os.node5.net/ + - https://www.node5.net + - https://www.blog.node5.net/ + - https://www.git.node5.net/ + # - https://www.ip.node5.net/ + - https://www.os.node5.net/ + - http://node5.net + - http://blog.node5.net/ + - http://git.node5.net/ + - http://ip.node5.net/ + - http://os.node5.net/ + - http://www.node5.net + - http://www.blog.node5.net/ + - http://www.git.node5.net/ + # - http://www.ip.node5.net/ + - http://www.os.node5.net/ + localhost: + desired_online: False + diff --git a/src/server_status.py b/src/server_status.py new file mode 100644 index 0000000..cba9bdd --- /dev/null +++ b/src/server_status.py @@ -0,0 +1,164 @@ +import telegram # Notifications +import asyncio # Telegram library is async +import requests # HTTP +import os # Unix socket file handling +import subprocess # Ping +import socket # Hostname, unix socket +import yaml # Config file +import logging # Log messages + +logger = logging.getLogger('server_status') +logger.setLevel(level=logging.DEBUG) + +with open("config.yml", "r") as file: + config = yaml.safe_load(file) + +# Set the path for the Unix socket +# without reinitialising entire script +# Used for SystemD timer to invoke status check, +socket_path = "/tmp/server_status.sock" +bot = telegram.Bot(config["telegram"]["token"]) +hostname = socket.gethostname() +# Used to only notify on state change +# Initialise data stucture based on targets in config file +state = {} +for host, host_config in config["hosts"].items(): + state[host] = {"online": None,"open_ports": []} + if 'urls' in host_config: + state[host]["http_status"] = {} + for url in host_config['urls']: + state[host]['http_status'][url] = None + +# Send message from non async function +def send_message(text): + logger.debug(f"Sending message: {text}") + asyncio.run(async_send_message(text)) + +async def async_send_message(text): + async with bot: + await bot.send_message(text=text, chat_id=config["telegram"]["chat_id"], + parse_mode=telegram.constants.ParseMode.MARKDOWN) + +def ping(host: str) -> bool: + response = subprocess.run(["ping", "-c", "1", host]) + return response.returncode == 0 + +def check_target(host: str, host_config: dict): + # Ping + online = ping(host) + # Default to desire host online, if not specified in config file + desired_online = host_config['desired_online'] if 'desired_online' in host_config else True + host_is_desired_state = desired_online == online + first_test = state[host]['online'] == None + changed_state = not first_test and state[host]['online'] != online + logger.debug(f''' + host_is_desired_state: {host_is_desired_state} + first_test: {first_test} + changed_state: {changed_state} + ''') + # notify if the host online state changed, or on fresh boot, if it isn't the desired online state + if changed_state or (first_test and not host_is_desired_state): + message = f"`{host}` is {'online' if online else 'offline'}" + logger.warning(message) + send_message(message) + state[host]['online'] = online # Save current state + + if not online: + return + + messages = [] + for url in host_config['urls']: + http_state = None + # HTTP Code + try: + #print(f''' + #---=== Getting: {url} ===--- + #''') + r = requests.get(url) + if r.status_code != 200: + http_state = r.status_code + except requests.exceptions.SSLError as exception: + try: + reason = exception.args[0].reason.args[0].verify_message + except Exception as exception: + logger.warning("Unable to get reason") + raise + http_state = "SSL error" + except Exception as exception: + raise + if http_state: + if state[host]['http_status'][url] is None or state[host]['http_status'][url] != http_state: + messages.append(f'[{url}]({url}) {http_state}') + state[host]['http_status'][url] = http_state + + + if len(messages) > 0: + send_message('\n'.join(messages)) # NTEINRIETNOIW NOIE#NOIE#N#IEN##I @NUY UYN@YUN@UY@NYU@N YUN@ UYN@YUN@YUN@YU@NUY@NU@NYU@N@UY@UNYUN@ + + +def check_status(): + try: + logger.info("Checking status") + for host, host_config in config["hosts"].items(): + check_target(host, host_config) + except Exception as ex: + send_message("Error getting status") + raise ex + + +def listen(): + # remove the socket file if it already exists + try: + os.unlink(socket_path) + except OSError: + if os.path.exists(socket_path): + raise + + try: + # Create the Unix socket server + server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + + # Bind the socket to the path + server.bind(socket_path) + + # Listen for incoming connections + server.listen(1) + + while True: + try: + # accept connections + logger.info("Server is listening for incoming connections...") + connection, client_address = server.accept() + + logger.debug("Connection from", str(connection).split(", ")[0][-4:]) + + # receive data from the client + while True: + data = connection.recv(1024) + if not data: + break + logger.debug("Received data:", data.decode()) + + # Send a response back to the client + response = "OK\n" + connection.sendall(response.encode()) + + check_status() + finally: + # Always close the connection if it"s been initialised + if "connection" in locals(): + connection.close() + finally: + # remove the socket file + os.unlink(socket_path) + + + +def main(): + # send_message(f"`{hostname}` online") + check_status() + listen() + +if __name__ == "__main__": + asyncio.run(main()) + |
