102 lines
4.2 KiB
Python
102 lines
4.2 KiB
Python
#!/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() |