Add pve-metrics/homelab-api.py

This commit is contained in:
2026-03-31 10:52:56 +00:00
parent 652a2bc94f
commit bd1f682948

102
pve-metrics/homelab-api.py Normal file
View File

@@ -0,0 +1,102 @@
#!/usr/bin/env python3
import json
import subprocess
import os
from http.server import BaseHTTPRequestHandler, HTTPServer
# --- CONFIGURATION FLAGS ---
PORT = 8080
IFACE = "bond0"
# Toggle features True/False depending on your setup
ENABLE_ZFS = False
ZPOOL = "rpool"
ENABLE_PVE_STATS = True # Set to False if not running on Proxmox
# ---------------------------
def run_cmd(cmd):
"""Runs a shell command and silently returns None if it fails."""
try:
return subprocess.check_output(cmd, shell=True, stderr=subprocess.DEVNULL).decode('utf-8').strip()
except Exception:
return None
class MetricsHandler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path == '/api/homelab':
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.send_header('Access-Control-Allow-Origin', '*')
self.end_headers()
data = {}
try:
# 1. Network & CPU (Includes 1-second delay for accuracy)
rx1 = int(run_cmd(f"cat /sys/class/net/{IFACE}/statistics/rx_bytes") or 0)
tx1 = int(run_cmd(f"cat /sys/class/net/{IFACE}/statistics/tx_bytes") or 0)
cpu_idle_str = run_cmd("vmstat 1 2 | tail -1 | awk '{print $15}'")
cpu_idle = float(cpu_idle_str) if cpu_idle_str else 100.0
data["cpu"] = round(100.0 - cpu_idle, 1)
rx2 = int(run_cmd(f"cat /sys/class/net/{IFACE}/statistics/rx_bytes") or 0)
tx2 = int(run_cmd(f"cat /sys/class/net/{IFACE}/statistics/tx_bytes") or 0)
data["rx_mb"] = round((rx2 - rx1) / (1024 * 1024), 2)
data["tx_mb"] = round((tx2 - tx1) / (1024 * 1024), 2)
# 2. RAM Usage
ram_str = run_cmd("free | grep Mem | awk '{print $3/$2 * 100.0}'")
data["ram"] = round(float(ram_str), 1) if ram_str else 0.0
# 3. Load Average (Pure Python)
load1, load5, load15 = os.getloadavg()
data["load_avg"] = [round(load1, 2), round(load5, 2), round(load15, 2)]
# 4. Uptime in Days (Pure Python)
with open('/proc/uptime', 'r') as f:
uptime_seconds = float(f.readline().split()[0])
data["uptime_days"] = round(uptime_seconds / 86400, 1)
# 5. Root Disk Usage (Pure Python, lightning fast)
st = os.statvfs('/')
total = st.f_blocks * st.f_frsize
free = st.f_bavail * st.f_frsize
data["root_disk_pct"] = round(((total - free) / total) * 100.0, 1)
# 6. CPU Temperature (Reads native thermal zone)
try:
with open('/sys/class/thermal/thermal_zone0/temp', 'r') as f:
data["temp_c"] = round(int(f.read()) / 1000.0, 1)
except Exception:
data["temp_c"] = "N/A" # Fails safely if hardware doesn't expose it
# 7. ZFS Array (Optional)
if ENABLE_ZFS:
zfs_alloc = run_cmd(f"zpool list -H -p -o allocated {ZPOOL}")
zfs_size = run_cmd(f"zpool list -H -p -o size {ZPOOL}")
if zfs_alloc and zfs_size:
data["zfs_pct"] = round((float(zfs_alloc) / float(zfs_size)) * 100.0, 1)
else:
data["zfs_pct"] = "Error"
# 8. Proxmox VM/LXC Counts (Optional)
if ENABLE_PVE_STATS:
data["vms_running"] = int(run_cmd("qm list | grep running | wc -l") or 0)
data["lxcs_running"] = int(run_cmd("pct list | grep running | wc -l") or 0)
# Send payload
self.wfile.write(json.dumps(data).encode())
except Exception as e:
self.wfile.write(json.dumps({"error": str(e)}).encode())
else:
self.send_response(404)
self.end_headers()
if __name__ == '__main__':
server = HTTPServer(('0.0.0.0', PORT), MetricsHandler)
print(f"Starting homelab API on port {PORT}...")
server.serve_forever()