Neue Tools: tailscale.py (Netzwerk-Status) + grafana.py (Dashboard-Status)

This commit is contained in:
root 2026-03-16 16:08:24 +07:00
parent 4b8ee105de
commit 1a88c782d6
2 changed files with 140 additions and 0 deletions

View file

@ -0,0 +1,73 @@
"""Grafana Monitoring Dashboard Tool."""
import requests
TOOLS = [
{
"type": "function",
"function": {
"name": "get_grafana_status",
"description": "Grafana Monitoring-Status: Health, Dashboards, Datasources, Alerts. Nutze bei 'Grafana', 'Monitoring Dashboard', 'Alerts'.",
"parameters": {"type": "object", "properties": {}, "required": []},
},
},
]
GRAFANA_URL = "http://100.109.206.43:3000"
GRAFANA_USER = "admin"
GRAFANA_PASS = "astral66"
def _api(path):
try:
r = requests.get(
f"{GRAFANA_URL}{path}",
auth=(GRAFANA_USER, GRAFANA_PASS),
timeout=10,
)
if r.ok:
return r.json()
return None
except Exception:
return None
def handle_get_grafana_status(**kw):
health = _api("/api/health")
if not health:
return "Grafana nicht erreichbar (http://100.109.206.43:3000)"
lines = [f"Grafana v{health.get('version', '?')} — DB: {health.get('database', '?')}"]
lines.append(f"URL: https://grafana.orbitalo.net")
datasources = _api("/api/datasources") or []
lines.append(f"\nDatasources: {len(datasources)}")
if datasources:
for ds in datasources:
lines.append(f" {ds.get('name', '?')} ({ds.get('type', '?')}) — {'aktiv' if ds.get('access') else 'inaktiv'}")
else:
lines.append(" Keine konfiguriert — Prometheus muss noch als Datasource hinzugefuegt werden")
dashboards = _api("/api/search?type=dash-db") or []
lines.append(f"\nDashboards: {len(dashboards)}")
for db in dashboards[:10]:
lines.append(f" {db.get('title', '?')} (/{db.get('uri', '?')})")
if not dashboards:
lines.append(" Keine Dashboards vorhanden")
alerts = _api("/api/alertmanager/grafana/api/v2/alerts") or []
if alerts:
firing = [a for a in alerts if a.get("status", {}).get("state") == "active"]
lines.append(f"\nAlerts: {len(alerts)} gesamt, {len(firing)} aktiv")
for a in firing[:5]:
labels = a.get("labels", {})
lines.append(f" {labels.get('alertname', '?')}{labels.get('severity', '?')}")
else:
lines.append("\nAlerts: keine")
return "\n".join(lines)
HANDLERS = {
"get_grafana_status": handle_get_grafana_status,
}

View file

@ -0,0 +1,67 @@
"""Tailscale Netzwerk-Status Tool."""
import json
import subprocess
TOOLS = [
{
"type": "function",
"function": {
"name": "get_tailscale_status",
"description": "Tailscale VPN Netzwerk-Status: Welche Geraete/Server sind online oder offline? Nutze bei 'ist Server X erreichbar', 'welche Geraete sind online', 'Netzwerk-Status', 'VPN'.",
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string", "description": "Optional: Name filtern (z.B. 'pve', 'gold', 'wordpress')", "default": ""}
},
"required": [],
},
},
},
]
def handle_get_tailscale_status(query="", **kw):
try:
out = subprocess.check_output(
["tailscale", "status", "--json"], text=True, timeout=10
)
except Exception as e:
return f"Tailscale nicht verfuegbar: {e}"
d = json.loads(out)
peers = d.get("Peer", {})
devices = []
for key, p in peers.items():
name = p.get("HostName", "?")
ip = p.get("TailscaleIPs", ["?"])[0]
online = p.get("Online", False)
last = p.get("LastSeen", "")[:10]
devices.append({"name": name, "ip": ip, "online": online, "last": last})
if query:
q = query.lower()
devices = [d for d in devices if q in d["name"].lower() or q in d["ip"]]
online = [d for d in devices if d["online"]]
offline = [d for d in devices if not d["online"]]
lines = [f"Tailscale: {len(online)} online, {len(offline)} offline (von {len(devices)} Geraeten)"]
if online:
lines.append(f"\n=== ONLINE ({len(online)}) ===")
for d in sorted(online, key=lambda x: x["name"]):
lines.append(f" {d['name']:28s} {d['ip']}")
if offline:
lines.append(f"\n=== OFFLINE ({len(offline)}) ===")
for d in sorted(offline, key=lambda x: x["name"]):
lines.append(f" {d['name']:28s} {d['ip']:18s} zuletzt: {d['last']}")
return "\n".join(lines)
HANDLERS = {
"get_tailscale_status": handle_get_tailscale_status,
}