From 586bedc0ebcbef13aaf63eb0542b34267d474a5b Mon Sep 17 00:00:00 2001 From: root Date: Mon, 9 Mar 2026 14:04:09 +0700 Subject: [PATCH] prometheus_client: alle Filesysteme anzeigen (ZFS, LVM, NVMe) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root-Partition + Extra-Datastores (ZFS-Pools, /var/lib/vz etc.) werden jetzt korrekt in Übersicht und Detail angezeigt. Made-with: Cursor --- homelab-ai-bot/core/prometheus_client.py | 73 +++++++++++++++++++++--- 1 file changed, 64 insertions(+), 9 deletions(-) diff --git a/homelab-ai-bot/core/prometheus_client.py b/homelab-ai-bot/core/prometheus_client.py index 8df479ba..6a7b5e1c 100644 --- a/homelab-ai-bot/core/prometheus_client.py +++ b/homelab-ai-bot/core/prometheus_client.py @@ -86,7 +86,7 @@ def get_disk(host: str = None) -> list[dict]: def get_disk_bytes(host: str = None) -> list[dict]: - """Returns available and total bytes per host.""" + """Returns available and total bytes for root partition per host.""" filt = f', host="{host}"' if host else "" avail = _by_host(instant_query( f'node_filesystem_avail_bytes{{mountpoint="/", host!=""{filt}}}'), "avail_bytes") @@ -104,6 +104,47 @@ def get_disk_bytes(host: str = None) -> list[dict]: return result +def get_all_filesystems(host: str = None) -> list[dict]: + """All non-trivial filesystems (skips tmpfs, dev, run, boot).""" + filt = f', host="{host}"' if host else "" + skip = "tmpfs|devtmpfs|efivarfs" + q_avail = f'node_filesystem_avail_bytes{{fstype!~"{skip}", host!=""{filt}}}' + q_total = f'node_filesystem_size_bytes{{fstype!~"{skip}", host!=""{filt}}}' + + data_a = instant_query(q_avail) + data_t = instant_query(q_total) + if data_a.get("status") != "success": + return [] + + total_map = {} + for r in data_t.get("data", {}).get("result", []): + m = r.get("metric", {}) + key = (m.get("host", m.get("instance", "?")), m.get("mountpoint", "")) + total_map[key] = float(r.get("value", [0, 0])[1]) + + results = [] + for r in data_a.get("data", {}).get("result", []): + m = r.get("metric", {}) + h = m.get("host", m.get("instance", "?")) + mp = m.get("mountpoint", "") + if mp in ("/boot/efi", "/boot"): + continue + avail = float(r.get("value", [0, 0])[1]) + total = total_map.get((h, mp), 0) + if total < 500 * 1024 * 1024: + continue + used_pct = ((total - avail) / total * 100) if total > 0 else 0 + results.append({ + "host": h, + "mountpoint": mp, + "total_gb": total / (1024**3), + "avail_gb": avail / (1024**3), + "used_pct": used_pct, + "device": m.get("device", ""), + }) + return sorted(results, key=lambda x: (x["host"], x["mountpoint"])) + + def get_uptime(host: str = None) -> list[dict]: filt = f', host="{host}"' if host else "" q = f'node_time_seconds{{host!=""{filt}}} - node_boot_time_seconds{{host!=""{filt}}}' @@ -150,6 +191,11 @@ def format_overview() -> str: disk_gb = {r["host"]: r for r in get_disk_bytes()} uptime = {r["host"]: r["value"] for r in get_uptime()} load = {r["host"]: r["value"] for r in get_load()} + all_fs = get_all_filesystems() + extra_fs = {} + for fs in all_fs: + if fs["mountpoint"] != "/": + extra_fs.setdefault(fs["host"], []).append(fs) hosts = sorted(set(list(cpu.keys()) + list(mem.keys()) + list(disk.keys()))) if not hosts: @@ -177,10 +223,17 @@ def format_overview() -> str: mem_str = f"{m:.0f}%" if m >= 0 else "n/a" load_str = f"{l5:.1f}" if l5 >= 0 else "n/a" + extra_line = "" + if h in extra_fs: + parts = [] + for efs in extra_fs[h]: + parts.append(f"{efs['mountpoint']}: {efs['avail_gb']:.0f}/{efs['total_gb']:.0f} GB frei") + extra_line = "\n Storage: " + ", ".join(parts) + lines.append( f"{emoji} {h}{warn}\n" f" CPU: {cpu_str} RAM: {mem_str} Disk: {disk_str}\n" - f" Load5: {load_str} Uptime: {_fmt_uptime(u)}" + f" Load5: {load_str} Uptime: {_fmt_uptime(u)}{extra_line}" ) warnings = get_warnings() @@ -200,8 +253,7 @@ def format_host_detail(host: str) -> str: cpu = get_cpu(host) mem = get_memory(host) - disk = get_disk(host) - disk_gb = get_disk_bytes(host) + filesystems = get_all_filesystems(host) uptime = get_uptime(host) load = get_load(host) @@ -213,11 +265,14 @@ def format_host_detail(host: str) -> str: lines.append(f"CPU: {cpu[0]['value']:.1f}%") if mem: lines.append(f"RAM: {mem[0]['value']:.1f}%") - if disk: - d_str = f"Disk: {disk[0]['value']:.1f}%" - if disk_gb: - d_str += f" ({disk_gb[0]['avail_gb']:.1f} / {disk_gb[0]['total_gb']:.1f} GB frei)" - lines.append(d_str) + if filesystems: + lines.append("Speicher:") + for fs in filesystems: + warn = " ⚠️" if fs["used_pct"] >= WARN_DISK else "" + lines.append( + f" {fs['mountpoint']}: {fs['used_pct']:.0f}% belegt " + f"({fs['avail_gb']:.0f} / {fs['total_gb']:.0f} GB frei){warn}" + ) if load: lines.append(f"Load (5m): {load[0]['value']:.2f}") if uptime: