summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authoruser <user@node5.net>2024-01-02 05:16:27 +0100
committeruser <user@node5.net>2024-01-02 05:16:27 +0100
commit937a5571a158115060f062b066543132d8c66752 (patch)
tree9ade99f4118dab905197d467b82796beaf44b6c8 /src
initial commit. Ping and http status works + state change
Diffstat (limited to 'src')
-rwxr-xr-xsrc/check_server_status.sh2
-rw-r--r--src/config.template.yml29
-rw-r--r--src/server_status.py164
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())
+