feat(proxmox): Counter mit _total-Suffix, mem_usage_percent, swap, diskread/write; gegen pvesh-Reala usgabe
This commit is contained in:
1 parent
72756babae
commit
d2fa6420be
1 file changed
+41
-19
+41
-19
@@ -1,13 +1,19 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# proxmox-ve.sh — VM- und Container-Metriken direkt vom PVE-Host.
|
# proxmox-ve.sh — VM- und Container-Metriken direkt vom PVE-Host.
|
||||||
#
|
#
|
||||||
# Läuft als root auf dem PVE-Knoten und nutzt `pvesh` (lokale API mit vollen
|
# Läuft als root auf dem PVE-Knoten und nutzt `pvesh` (lokale API, volle
|
||||||
# Rechten). Damit entfällt der API-Token komplett — kein Sys.Audit, keine ACL.
|
# Rechte). Damit entfällt der API-Token komplett — kein Sys.Audit, keine ACL.
|
||||||
#
|
#
|
||||||
# Ausgabe im Prometheus-Format (data_format = "prometheus" in der Telegraf-Conf):
|
# Warum nicht inputs.proxmox: Das Plugin fragt zuerst /nodes/<node>/dns ab.
|
||||||
# proxmox_vm_cpu_usage{vmid="1001",name="tunnel",type="qemu",status="running"} 0.42
|
# Fehlt dort Sys.Audit, liefert es KOMPLETT nichts — ohne Logmeldung.
|
||||||
#
|
#
|
||||||
# Aufruf durch Telegraf (inputs.exec) oder manuell zum Testen.
|
# Ausgabe im Prometheus-Format (data_format = "prometheus"):
|
||||||
|
# proxmox_vm_cpu_usage{vmid="1001",name="tunnel",type="qemu",status="running"} 0.38
|
||||||
|
#
|
||||||
|
# Getestet gegen die reale Ausgabe von pvesh get /cluster/resources:
|
||||||
|
# - Top-Level ist ein ARRAY (kein {"data":...}-Wrapper)
|
||||||
|
# - qemu nutzt "cpus", lxc zusätzlich "maxswap"/"swap"
|
||||||
|
# - mem kann maxmem überschreiten (Ballooning/VirtIO)
|
||||||
|
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
@@ -18,46 +24,62 @@ pvesh get /cluster/resources --output-format json > "$TMP" 2>/dev/null || true
|
|||||||
[ -s "$TMP" ] || exit 0
|
[ -s "$TMP" ] || exit 0
|
||||||
|
|
||||||
python3 - "$TMP" <<'PY'
|
python3 - "$TMP" <<'PY'
|
||||||
import json, sys, re
|
import json, sys
|
||||||
|
|
||||||
def esc(s):
|
def esc(s):
|
||||||
return str(s).replace('\\', '\\\\').replace('"', '\\"').replace('\n', ' ')
|
return str(s).replace('\\', '\\\\').replace('"', '\\"').replace('\n', ' ')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = json.load(open(sys.argv[1]))
|
raw = json.load(open(sys.argv[1]))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"# JSON-Fehler: {e}", file=sys.stderr); sys.exit(0)
|
print(f"# JSON-Fehler: {e}", file=sys.stderr); sys.exit(0)
|
||||||
|
|
||||||
rows = data.get("data", data) if isinstance(data, dict) else data
|
# pvesh liefert je nach Aufruf {"data":[...]} ODER direkt [...]
|
||||||
|
rows = raw.get("data", raw) if isinstance(raw, dict) else raw
|
||||||
if not isinstance(rows, list):
|
if not isinstance(rows, list):
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
def emit(name, labels, value):
|
def emit(name, labels, value):
|
||||||
if value is None: return
|
if value is None:
|
||||||
lab = ",".join(f'{k}="{esc(v)}"' for k, v in labels.items() if v is not None)
|
return
|
||||||
|
lab = ",".join(f'{k}="{esc(v)}"' for k, v in labels.items() if v is not None and v != "")
|
||||||
print(f"{name}{{{lab}}} {value}")
|
print(f"{name}{{{lab}}} {value}")
|
||||||
|
|
||||||
for r in rows:
|
for r in rows:
|
||||||
typ = r.get("type")
|
typ = r.get("type")
|
||||||
if typ not in ("qemu", "lxc"): # node/storage überspringen
|
if typ not in ("qemu", "lxc"): # node/storage/sdn überspringen
|
||||||
continue
|
continue
|
||||||
if r.get("template"): # Templates nicht als Gast zählen
|
if r.get("template"): # Templates nicht als Gast zählen
|
||||||
continue
|
continue
|
||||||
if str(r.get("vmid", "")) == "": # nur Gäste mit vmid
|
if r.get("vmid") in (None, ""):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
L = {"vmid": r.get("vmid"), "name": r.get("name"), "node": r.get("node"),
|
L = {"vmid": r.get("vmid"), "name": r.get("name"), "node": r.get("node"),
|
||||||
"type": typ, "status": r.get("status")}
|
"type": typ, "status": r.get("status")}
|
||||||
# cpu ist ein Anteil 0..1 der zugewiesenen Kerne
|
|
||||||
cpu = r.get("cpu")
|
cpu = r.get("cpu")
|
||||||
if cpu is not None:
|
if cpu is not None:
|
||||||
emit("proxmox_vm_cpu_usage", L, round(float(cpu) * 100, 2))
|
emit("proxmox_vm_cpu_usage_percent", L, round(float(cpu) * 100, 2))
|
||||||
emit("proxmox_vm_cpu_cores", L, r.get("maxcpu"))
|
|
||||||
|
# qemu: "cpus"; lxc: "cpus" oder "maxcpu"
|
||||||
|
emit("proxmox_vm_cpu_cores", L, r.get("cpus") or r.get("maxcpu"))
|
||||||
|
|
||||||
emit("proxmox_vm_mem_bytes", L, r.get("mem"))
|
emit("proxmox_vm_mem_bytes", L, r.get("mem"))
|
||||||
emit("proxmox_vm_mem_max_bytes", L, r.get("maxmem"))
|
emit("proxmox_vm_mem_max_bytes", L, r.get("maxmem"))
|
||||||
|
emit("proxmox_vm_swap_bytes", L, r.get("swap"))
|
||||||
|
emit("proxmox_vm_swap_max_bytes", L, r.get("maxswap"))
|
||||||
emit("proxmox_vm_disk_bytes", L, r.get("disk"))
|
emit("proxmox_vm_disk_bytes", L, r.get("disk"))
|
||||||
emit("proxmox_vm_disk_max_bytes", L, r.get("maxdisk"))
|
emit("proxmox_vm_disk_max_bytes", L, r.get("maxdisk"))
|
||||||
emit("proxmox_vm_net_in_bytes", L, r.get("netin"))
|
emit("proxmox_vm_diskread_bytes_total", L, r.get("diskread"))
|
||||||
emit("proxmox_vm_net_out_bytes", L, r.get("netout"))
|
emit("proxmox_vm_diskwrite_bytes_total", L, r.get("diskwrite"))
|
||||||
|
emit("proxmox_vm_net_in_bytes_total", L, r.get("netin"))
|
||||||
|
emit("proxmox_vm_net_out_bytes_total", L, r.get("netout"))
|
||||||
emit("proxmox_vm_uptime_seconds", L, r.get("uptime"))
|
emit("proxmox_vm_uptime_seconds", L, r.get("uptime"))
|
||||||
emit("proxmox_vm_running", L, 1 if r.get("status") == "running" else 0)
|
emit("proxmox_vm_running", L, 1 if r.get("status") == "running" else 0)
|
||||||
|
|
||||||
|
# RAM-Auslastung in Prozent, robust gegen mem > maxmem (Ballooning)
|
||||||
|
mem, maxmem = r.get("mem"), r.get("maxmem")
|
||||||
|
if mem is not None and maxmem:
|
||||||
|
emit("proxmox_vm_mem_usage_percent", L,
|
||||||
|
round(min(100.0, float(mem) / float(maxmem) * 100.0), 2))
|
||||||
PY
|
PY
|
||||||
Reference in new issue
Block a user