ansible/scripts/beszel-setup-alerts.sh
ilia 0f34c51fc8
All checks were successful
CI / skip-ci-check (pull_request) Successful in 8s
CI / lint-and-test (pull_request) Successful in 17s
CI / secret-scanning (pull_request) Successful in 8s
CI / dependency-scan (pull_request) Successful in 18s
CI / ansible-validation (pull_request) Successful in 54s
CI / sast-scan (pull_request) Successful in 29s
CI / license-check (pull_request) Successful in 14s
CI / vault-check (pull_request) Successful in 13s
CI / container-scan (pull_request) Successful in 8s
CI / sonar-analysis (pull_request) Successful in 8s
CI / playbook-test (pull_request) Successful in 27s
CI / workflow-summary (pull_request) Successful in 6s
Complete homelab post-sprint: SSO docs, monitoring scripts, phase 0/1 closure.
Consolidate sprint status into handoff docs, add Listmonk/Mattermost/Mailcow
and Vikunja SSO guides, Beszel alerts script, mattermost inventory, and
mark phases 0–1 complete with phase 2 backlog for edge Caddy and security.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-24 12:13:55 -04:00

133 lines
4.1 KiB
Bash
Executable File

#!/usr/bin/env bash
# Enable Beszel alerts (Status, CPU, Memory, Disk) on all monitored systems.
#
# Prerequisite: admin account + SMTP configured (./scripts/beszel-setup-smtp.sh)
#
# Usage:
# export BESZEL_URL=http://10.0.10.22:8090
# export BESZEL_EMAIL=you@example.com
# export BESZEL_PASSWORD='your-beszel-password'
# ./scripts/beszel-setup-alerts.sh
#
# Optional thresholds (percent unless noted):
# BESZEL_CPU_THRESHOLD=80
# BESZEL_MEM_THRESHOLD=85
# BESZEL_DISK_THRESHOLD=90
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
BESZEL_URL="${BESZEL_URL:-http://10.0.10.22:8090}"
BESZEL_EMAIL="${BESZEL_EMAIL:-}"
BESZEL_PASSWORD="${BESZEL_PASSWORD:-}"
BESZEL_CPU_THRESHOLD="${BESZEL_CPU_THRESHOLD:-80}"
BESZEL_MEM_THRESHOLD="${BESZEL_MEM_THRESHOLD:-85}"
BESZEL_DISK_THRESHOLD="${BESZEL_DISK_THRESHOLD:-90}"
if [[ -z "${BESZEL_EMAIL}" || -z "${BESZEL_PASSWORD}" ]] && [[ -f "${REPO_ROOT}/.env" ]]; then
# shellcheck disable=SC1091
set +u
set -a
# shellcheck source=/dev/null
source "${REPO_ROOT}/.env"
set +a
set -u
BESZEL_EMAIL="${BESZEL_EMAIL:-}"
BESZEL_PASSWORD="${BESZEL_PASSWORD:-}"
fi
if [[ -z "${BESZEL_EMAIL}" || -z "${BESZEL_PASSWORD}" ]]; then
echo "Set BESZEL_EMAIL and BESZEL_PASSWORD (Beszel admin)" >&2
exit 1
fi
export BESZEL_URL BESZEL_EMAIL BESZEL_PASSWORD
export BESZEL_CPU_THRESHOLD BESZEL_MEM_THRESHOLD BESZEL_DISK_THRESHOLD
"${REPO_ROOT}/.venv/bin/python3" <<'PY'
import json
import os
import sys
import urllib.error
import urllib.parse
import urllib.request
base = os.environ["BESZEL_URL"].rstrip("/")
email = os.environ["BESZEL_EMAIL"]
password = os.environ["BESZEL_PASSWORD"]
cpu_threshold = float(os.environ["BESZEL_CPU_THRESHOLD"])
mem_threshold = float(os.environ["BESZEL_MEM_THRESHOLD"])
disk_threshold = float(os.environ["BESZEL_DISK_THRESHOLD"])
ALERTS = [
("Status", None),
("CPU", cpu_threshold),
("Memory", mem_threshold),
("Disk", disk_threshold),
]
def req(method, path, token=None, body=None):
url = f"{base}{path}"
data = None
headers = {"Content-Type": "application/json"}
if body is not None:
data = json.dumps(body).encode()
if token:
headers["Authorization"] = token
request = urllib.request.Request(url, data=data, headers=headers, method=method)
try:
with urllib.request.urlopen(request, timeout=30) as resp:
raw = resp.read().decode()
return resp.status, json.loads(raw) if raw else {}
except urllib.error.HTTPError as e:
raw = e.read().decode()
print(f"HTTP {e.code} {path}: {raw}", file=sys.stderr)
raise
print(f"Login to Beszel at {base} as {email}...")
token = None
for collection in ("_superusers", "users"):
try:
_, auth = req(
"POST",
f"/api/collections/{collection}/auth-with-password",
body={"identity": email, "password": password},
)
token = auth.get("token")
if token:
print(f"Login OK ({collection})")
break
except urllib.error.HTTPError:
continue
if not token:
print("Login failed: check BESZEL_EMAIL and BESZEL_PASSWORD", file=sys.stderr)
sys.exit(1)
query = urllib.parse.urlencode({"perPage": 500, "fields": "id,name"})
_, systems_resp = req("GET", f"/api/collections/systems/records?{query}", token=token)
systems = systems_resp.get("items", [])
if not systems:
print("No systems found in Beszel hub", file=sys.stderr)
sys.exit(1)
system_ids = [s["id"] for s in systems]
names = ", ".join(s.get("name", s["id"]) for s in systems)
print(f"Found {len(system_ids)} systems: {names}")
for alert_name, threshold in ALERTS:
body = {"name": alert_name, "systems": system_ids}
if threshold is not None:
body["value"] = threshold
req("POST", "/api/beszel/user-alerts", token=token, body=body)
if threshold is None:
print(f" ✅ {alert_name} (down detection)")
else:
print(f" ✅ {alert_name} > {threshold}%")
print("\nDone. Alerts apply to all systems. Verify in hub UI (bell icon) or send test:")
print(f" curl -X POST {base}/api/beszel/test-notification -H 'Authorization: {token[:20]}...'")
PY