kuma-inventory.py printed the junk url field (https://) instead of the hostname for ping monitors — hid ~100 monitors from FQDN matching. kuma-add-ping.py: idempotent ping-monitor adds (exists-check by name and target). Matrix published to Discourse t/309 (canonical, #343). Detail: https://projects.knownelement.com/issues/343
This commit is contained in:
@@ -0,0 +1,92 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""kuma-add-ping.py — add ping monitors to Uptime Kuma by name [#435][#705]
|
||||||
|
|
||||||
|
Companion to kuma-inventory.py (same connect pattern). Reads a list of
|
||||||
|
hostnames from argv or a file (one per line), skips any that already have
|
||||||
|
a ping monitor with the same name or target, and adds the rest with the
|
||||||
|
fleet-standard ping profile. Print-only summary; never deletes.
|
||||||
|
|
||||||
|
Env (from ~/.creds/uptime-kuma.env):
|
||||||
|
UPTIME_KUMA_URL, UPTIME_KUMA_TOKEN
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python3 kuma-add-ping.py host1.knel.net host2.knel.net
|
||||||
|
python3 kuma-add-ping.py --file hosts.txt
|
||||||
|
"""
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
|
||||||
|
import socketio
|
||||||
|
|
||||||
|
INTERVAL = 60 # fleet-standard ping cadence (seconds)
|
||||||
|
|
||||||
|
|
||||||
|
def kuma_connect():
|
||||||
|
sio = socketio.Client(reconnection=False)
|
||||||
|
state = {}
|
||||||
|
|
||||||
|
@sio.on("monitorList")
|
||||||
|
def _ml(d):
|
||||||
|
state["ml"] = d
|
||||||
|
|
||||||
|
sio.connect(
|
||||||
|
os.environ["UPTIME_KUMA_URL"], transports=["websocket"], wait_timeout=30
|
||||||
|
)
|
||||||
|
r = sio.call("loginByToken", os.environ["UPTIME_KUMA_TOKEN"], timeout=30)
|
||||||
|
if not (isinstance(r, dict) and r.get("ok")):
|
||||||
|
raise SystemExit(f"login failed: {r}")
|
||||||
|
sio.emit("monitorList")
|
||||||
|
for _ in range(20):
|
||||||
|
if "ml" in state:
|
||||||
|
break
|
||||||
|
time.sleep(1)
|
||||||
|
return sio, state.get("ml", {})
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
ap = argparse.ArgumentParser()
|
||||||
|
ap.add_argument("hosts", nargs="*")
|
||||||
|
ap.add_argument("--file", help="file with one hostname per line")
|
||||||
|
args = ap.parse_args()
|
||||||
|
|
||||||
|
hosts = list(args.hosts)
|
||||||
|
if args.file:
|
||||||
|
with open(args.file) as fh:
|
||||||
|
hosts += [ln.strip() for ln in fh if ln.strip()]
|
||||||
|
if not hosts:
|
||||||
|
raise SystemExit("no hosts given")
|
||||||
|
|
||||||
|
sio, mons = kuma_connect()
|
||||||
|
existing_names = {(m.get("name") or "").lower() for m in mons.values()}
|
||||||
|
existing_targets = {(m.get("hostname") or m.get("url") or "").lower()
|
||||||
|
for m in mons.values()}
|
||||||
|
|
||||||
|
added = skipped = 0
|
||||||
|
for host in hosts:
|
||||||
|
if host.lower() in existing_names or host.lower() in existing_targets:
|
||||||
|
print(f" = {host} (monitor exists)")
|
||||||
|
skipped += 1
|
||||||
|
continue
|
||||||
|
res = sio.call("add", {
|
||||||
|
"type": "ping",
|
||||||
|
"name": host,
|
||||||
|
"hostname": host,
|
||||||
|
"interval": INTERVAL,
|
||||||
|
"retryInterval": 60,
|
||||||
|
"maxretries": 2,
|
||||||
|
"notificationIDList": {},
|
||||||
|
}, timeout=30)
|
||||||
|
if isinstance(res, dict) and res.get("ok"):
|
||||||
|
print(f" + {host}")
|
||||||
|
added += 1
|
||||||
|
else:
|
||||||
|
print(f" ! {host}: {res}")
|
||||||
|
|
||||||
|
print(f"done: +{added} added, ={skipped} skipped, "
|
||||||
|
f"{len(hosts) - added - skipped} failed")
|
||||||
|
sio.disconnect()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -63,7 +63,8 @@ def main():
|
|||||||
mtype = m.get("type") or "?"
|
mtype = m.get("type") or "?"
|
||||||
active = "on" if m.get("active", True) else "PAUSED"
|
active = "on" if m.get("active", True) else "PAUSED"
|
||||||
name = (m.get("name") or "?").replace("\t", " ")
|
name = (m.get("name") or "?").replace("\t", " ")
|
||||||
target = (m.get("hostname") if m.get("type") == "dns" else None) or m.get("url") or m.get("hostname") or ""
|
# hostname first: ping monitors carry a junk default url ("https://")
|
||||||
|
target = m.get("hostname") or m.get("url") or ""
|
||||||
target = str(target).replace("\t", " ")
|
target = str(target).replace("\t", " ")
|
||||||
rows.append((mid, parent, mtype, active, name, target))
|
rows.append((mid, parent, mtype, active, name, target))
|
||||||
rows.sort(key=lambda r: (r[1] == "-", r[1], r[4].lower()))
|
rows.sort(key=lambda r: (r[1] == "-", r[1], r[4].lower()))
|
||||||
|
|||||||
Reference in New Issue
Block a user