Files
SnarfCode/scripts/populate_uptime_kuma.py

171 lines
10 KiB
Python

import paramiko
import sys
import json
sys.stdout.reconfigure(encoding='utf-8')
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect('140.44.4.11', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
def run_cmd(cmd):
print(f"\n>>> {cmd}")
_, stdout, stderr = c.exec_command(cmd)
out = stdout.read().decode().strip()
err = stderr.read().decode().strip()
if out:
print(out)
if err:
print("[stderr]", err)
return out
pod = run_cmd("kubectl get pod -n monitoring -l app.kubernetes.io/name=uptime-kuma -o jsonpath='{.items[0].metadata.name}'")
# Remote python script to populate MariaDB
setup_script = '''
import json
import subprocess
def query_db(sql):
cmd = ["mariadb", "--socket=/app/data/run/mariadb.sock", "-u", "root", "kuma", "-e", sql]
res = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
if res.returncode != 0:
print("DB ERROR:", res.stderr)
return res.stdout
# 1. Ensure Gotify Notification Exists
notif_config = {
"name": "Gotify Infrastructure Alerts",
"type": "gotify",
"isDefault": True,
"gotifyServerUrl": "http://140.44.4.70:8090",
"gotifyApplicationToken": "A7zBx9InFrAaLeRtStOk",
"gotifyPriority": 8
}
notif_json = json.dumps(notif_config)
check_notif = query_db("SELECT id FROM notification WHERE name = 'Gotify Infrastructure Alerts';")
if "id" not in check_notif or not check_notif.split():
print("Creating Gotify Notification in Uptime Kuma...")
escaped_json = notif_json.replace("'", "''")
query_db(f"INSERT INTO notification (name, active, user_id, is_default, config) VALUES ('Gotify Infrastructure Alerts', 1, 1, 1, '{escaped_json}');")
notif_id_out = query_db("SELECT id FROM notification WHERE name = 'Gotify Infrastructure Alerts';")
notif_id = [line.strip() for line in notif_id_out.splitlines() if line.strip().isdigit()][0]
print(f"Gotify Notification ID: {notif_id}")
# 2. Define Monitors List (DNS + Direct VIP/IP)
monitors = [
# --- DNS INGRESS MONITORS ---
{"name": "Gotify Hub (DNS)", "type": "http", "url": "https://gotify.snarfnet.net", "interval": 60, "ignore_tls": 0},
{"name": "Heimdall Portal (DNS)", "type": "http", "url": "https://portal.snarfnet.net", "interval": 60, "ignore_tls": 0},
{"name": "NPM Admin Console (DNS)", "type": "http", "url": "https://nginx.snarfnet.net", "interval": 60, "ignore_tls": 1},
{"name": "Authentik SSO (DNS)", "type": "http", "url": "https://authentik.snarfnet.net", "interval": 60, "ignore_tls": 0, "status_codes": '["200-399"]'},
{"name": "Rancher Management (DNS)", "type": "http", "url": "https://aerodrome.snarfnet.net", "interval": 60, "ignore_tls": 1},
{"name": "Harvester Console (DNS)", "type": "http", "url": "https://harvester.spacedock.snarfnet.net", "interval": 60, "ignore_tls": 1},
{"name": "Gitea DevOps (DNS)", "type": "http", "url": "https://git.snarfnet.net", "interval": 60, "ignore_tls": 0},
{"name": "ArgoCD GitOps (DNS)", "type": "http", "url": "https://argo.snarfnet.net", "interval": 60, "ignore_tls": 0},
{"name": "AWX Automation (DNS)", "type": "http", "url": "https://awx.snarfnet.net", "interval": 60, "ignore_tls": 1},
{"name": "Semaphore Ansible (DNS)", "type": "http", "url": "https://semaphore.snarfnet.net", "interval": 60, "ignore_tls": 0},
{"name": "Jenkins CI/CD (DNS)", "type": "http", "url": "https://jenkins.snarfnet.net", "interval": 60, "ignore_tls": 0},
{"name": "Zabbix Web UI (DNS)", "type": "http", "url": "https://zabbix.snarfnet.net", "interval": 60, "ignore_tls": 0},
{"name": "Grafana Dashboards (DNS)", "type": "http", "url": "https://grafana.snarfnet.net", "interval": 60, "ignore_tls": 0},
{"name": "Longhorn Aerodrome (DNS)", "type": "http", "url": "https://longhorn.aerodrome.snarfnet.net", "interval": 60, "ignore_tls": 0},
{"name": "Longhorn Spaceport (DNS)", "type": "http", "url": "https://longhorn.spaceport.snarfnet.net", "interval": 60, "ignore_tls": 0},
{"name": "Home Assistant (DNS)", "type": "http", "url": "https://snarfhome.snarfnet.net", "interval": 60, "ignore_tls": 0},
{"name": "PeaNUT UPS Portal (DNS)", "type": "http", "url": "https://peanut.snarfnet.net", "interval": 60, "ignore_tls": 0},
{"name": "Open WebUI AI (DNS)", "type": "http", "url": "https://ai.snarfnet.net", "interval": 60, "ignore_tls": 0},
{"name": "SnarfNAS DSM (DNS)", "type": "http", "url": "https://snarfnas.snarfnet.net", "interval": 60, "ignore_tls": 1},
{"name": "SN-NAS DSM (DNS)", "type": "http", "url": "https://sn-nas.snarfnet.net", "interval": 60, "ignore_tls": 1},
{"name": "Homepage (DNS)", "type": "http", "url": "https://homepage.snarfnet.net", "interval": 60, "ignore_tls": 0},
{"name": "MeshCentral (DNS)", "type": "http", "url": "https://mesh.snarfnet.net", "interval": 60, "ignore_tls": 1},
{"name": "Homebox (DNS)", "type": "http", "url": "https://homebox.snarfnet.net", "interval": 60, "ignore_tls": 0},
# --- DIRECT VIP & IP MONITORS ---
{"name": "Swarm VIP - Gotify (VIP)", "type": "http", "url": "http://140.44.4.70:8090", "interval": 30, "ignore_tls": 0},
{"name": "Swarm VIP - Heimdall (VIP)", "type": "http", "url": "http://140.44.4.70:85", "interval": 30, "ignore_tls": 0},
{"name": "Swarm VIP - Homebox (VIP)", "type": "http", "url": "http://140.44.4.70:3100", "interval": 30, "ignore_tls": 0},
{"name": "Swarm VIP - PeaNUT (VIP)", "type": "http", "url": "http://140.44.4.70:8050", "interval": 30, "ignore_tls": 0},
{"name": "Swarm Host - NPM (VIP)", "type": "http", "url": "http://140.44.4.74:81", "interval": 30, "ignore_tls": 0},
{"name": "Aerodrome VIP - Authentik (VIP)", "type": "http", "url": "https://140.44.4.29:444", "interval": 30, "ignore_tls": 1, "status_codes": '["200-399"]'},
{"name": "Aerodrome VIP - AWX (VIP)", "type": "http", "url": "https://140.44.4.30:443", "interval": 30, "ignore_tls": 1},
{"name": "Aerodrome VIP - Grafana (VIP)", "type": "http", "url": "http://140.44.4.32:88", "interval": 30, "ignore_tls": 0},
{"name": "Aerodrome VIP - Longhorn (VIP)", "type": "http", "url": "http://140.44.4.36:80", "interval": 30, "ignore_tls": 0},
{"name": "Aerodrome VIP - Traefik (VIP)", "type": "http", "url": "https://140.44.4.34:443", "interval": 30, "ignore_tls": 1},
{"name": "Spaceport VIP - Zabbix Web (VIP)", "type": "http", "url": "http://140.44.4.57:8888", "interval": 30, "ignore_tls": 0},
{"name": "Spaceport VIP - Semaphore (VIP)", "type": "http", "url": "http://140.44.4.58:100", "interval": 30, "ignore_tls": 0},
{"name": "Spaceport VIP - Jenkins (VIP)", "type": "http", "url": "http://140.44.4.59:101", "interval": 30, "ignore_tls": 0},
{"name": "Spaceport VIP - Homepage (VIP)", "type": "http", "url": "http://140.44.4.60:8040", "interval": 30, "ignore_tls": 0},
{"name": "Spaceport VIP - Gitea (VIP)", "type": "http", "url": "http://140.44.4.62:90", "interval": 30, "ignore_tls": 0},
{"name": "Spaceport Node - Longhorn (VIP)", "type": "http", "url": "http://140.44.4.41:80", "interval": 30, "ignore_tls": 0},
{"name": "Home Assistant Core (IP)", "type": "http", "url": "http://14.4.4.20:8123", "interval": 30, "ignore_tls": 0},
{"name": "AI Hailo Engine (IP)", "type": "http", "url": "http://140.44.4.7:8000/docs", "interval": 30, "ignore_tls": 0},
{"name": "AI Open WebUI (IP)", "type": "http", "url": "http://140.44.4.8:8080", "interval": 30, "ignore_tls": 0},
{"name": "Harvester HCI Console (VIP)", "type": "http", "url": "https://140.44.4.90:443", "interval": 30, "ignore_tls": 1},
{"name": "Synology NAS 1 DSM (IP)", "type": "http", "url": "https://14.10.10.71:5001", "interval": 30, "ignore_tls": 1},
{"name": "SnarfNAS DSM (IP)", "type": "http", "url": "https://14.10.10.72:5001", "interval": 30, "ignore_tls": 1},
{"name": "NUT Master Daemon (TCP)", "type": "port", "hostname": "14.4.4.44", "port": 3493, "interval": 30},
]
# Fetch existing monitors
existing_out = query_db("SELECT id, name FROM monitor;")
existing_names = set()
for line in existing_out.splitlines()[1:]:
parts = line.split("\\t")
if len(parts) >= 2:
existing_names.add(parts[1].strip())
added_count = 0
for m in monitors:
name = m["name"]
if name in existing_names:
print(f"Skipping existing monitor: {name}")
continue
m_type = m["type"]
interval = m.get("interval", 60)
ignore_tls = m.get("ignore_tls", 0)
status_codes = m.get("status_codes", '["200-299"]')
if m_type == "http":
url = m["url"]
sql = f"""INSERT INTO monitor (name, type, active, user_id, `interval`, url, ignore_tls, accepted_statuscodes_json)
VALUES ('{name}', 'http', 1, 1, {interval}, '{url}', {ignore_tls}, '{status_codes}');"""
elif m_type == "port":
host = m["hostname"]
p = m["port"]
sql = f"""INSERT INTO monitor (name, type, active, user_id, `interval`, hostname, port)
VALUES ('{name}', 'port', 1, 1, {interval}, '{host}', {p});"""
query_db(sql)
added_count += 1
print(f"Added monitor: {name}")
# Link all monitors to Gotify notification in monitor_notification
all_monitors_out = query_db("SELECT id FROM monitor;")
for line in all_monitors_out.splitlines()[1:]:
line = line.strip()
if line.isdigit():
m_id = line
check_link = query_db(f"SELECT id FROM monitor_notification WHERE monitor_id = {m_id} AND notification_id = {notif_id};")
if "id" not in check_link or not check_link.split():
query_db(f"INSERT INTO monitor_notification (monitor_id, notification_id) VALUES ({m_id}, {notif_id});")
print(f"Successfully processed all monitors! Total new monitors added: {added_count}")
'''
# Deliver script via stdin into /tmp/populate_kuma.py on the node
stdin, _, _ = c.exec_command("cat > /tmp/populate_kuma.py")
stdin.write(setup_script)
stdin.close()
run_cmd(f"kubectl cp /tmp/populate_kuma.py monitoring/{pod}:/tmp/populate_kuma.py")
run_cmd(f"kubectl exec -n monitoring {pod} -- python3 /tmp/populate_kuma.py")
# Restart Uptime Kuma to load new monitors
print("\n=== Restarting Uptime Kuma Deployment ===")
run_cmd("kubectl rollout restart deployment/uptime-kuma-2-1756668961 -n monitoring")
run_cmd("kubectl rollout status deployment/uptime-kuma-2-1756668961 -n monitoring --timeout=90s")
c.close()
print("\n=== Uptime Kuma Population Complete ===")