Files
PFVCluster/scripts/kuma-regroup.py
T
mrcharles 8bc1dc6612 feat(kuma): Cloudron HTTP-200 coverage + group discipline [#435]
55 http monitors added under "Cloud Systems" (blue/green: canary first,
verified green); 9 root-level strays re-homed into founder's groups
(root now zero). New tools: inventory (read-only dump), cloudron-sync
(idempotent diff/add from committed app list), regroup (lib-based moves;
raw editMonitor times out on this build). fleet-sync now requires
--group-id so it can never place monitors at root again.

Results: https://projects.knownelement.com/issues/435#note-4
2026-09-02 13:23:27 -05:00

70 lines
2.5 KiB
Python

#!/usr/bin/env python3
"""kuma-regroup.py — move Uptime Kuma monitors into groups [#435]
Founder ruling 2026-09-02: no monitors at the root; use the existing
groups. Uses the uptime-kuma-api library — raw socket.io editMonitor with
a hand-built payload times out on this Kuma build (missing form fields
the server requires before acking; the lib sends the full UI-shaped
payload). dns_resolve_type="A" is passed to satisfy lib-side validation
for monitors where the field is None (harmless for non-dns types).
Env (from ~/.creds/uptime-kuma.env):
UPTIME_KUMA_URL, UPTIME_KUMA_TOKEN
Usage (host — needs python-socketio-compatible env via lib):
python3 kuma-regroup.py --dry-move 211:48 213:13
python3 kuma-regroup.py --move 211:48 213:13
id:groupid pairs. --move executes; --dry-move prints the plan only.
Verify afterwards with kuma-inventory.py (the lib's get_monitors does
not map the parent field — trust the raw monitorList view).
"""
import argparse
import os
import sys
from uptime_kuma_api import UptimeKumaApi
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--move", nargs="+", default=[], metavar="ID:GROUPID")
ap.add_argument("--dry-move", nargs="+", default=[], metavar="ID:GROUPID")
args = ap.parse_args()
pairs = [(int(a), int(b)) for a, b in (p.split(":") for p in (args.move or args.dry_move))]
dry = bool(args.dry_move)
api = UptimeKumaApi(os.environ["UPTIME_KUMA_URL"], timeout=30)
rc = 0
try:
api.login_by_token(os.environ["UPTIME_KUMA_TOKEN"])
monitors = {m["id"]: m for m in api.get_monitors()}
for mid, gid in pairs:
m = monitors.get(mid)
if not m:
print(f"SKIP {mid}: not found")
rc = 1
continue
g = monitors.get(gid)
gname = g["name"] if g else "?"
if dry:
print(f"DRY {mid} '{m['name']}' -> group {gid} '{gname}'")
continue
try:
r = api.edit_monitor(mid, parent=gid, dns_resolve_type="A")
ok = isinstance(r, dict) and ("Saved" in str(r.get("msg", "")))
print(f"MOVE {mid} '{m['name']}' -> {gid} '{gname}': {r.get('msg') if ok else r}")
if not ok:
rc = 1
except Exception as e: # noqa: BLE001 — per-move isolation, batch continues
print(f"MOVE {mid} FAIL: {str(e)[:120]}")
rc = 1
finally:
api.disconnect()
return rc
if __name__ == "__main__":
sys.exit(main())