diff --git a/stats_server.py b/stats_server.py index e17e366..b15363b 100644 --- a/stats_server.py +++ b/stats_server.py @@ -7,7 +7,7 @@ same 2s push interval. Services remain mocked until systemd integration is added import asyncio import json -import random +import subprocess import time from datetime import datetime from pathlib import Path @@ -61,16 +61,41 @@ def _get_net_throughput() -> tuple[float, float]: return rx_kbps, tx_kbps -def _mock_services() -> list[dict]: - """Mocked service status — same logic as mock_server.py.""" - return [ - {"name": "docker", "status": random.choice(["running", "running", "running", "stopped"])}, - {"name": "pihole", "status": random.choice(["running", "running", "running", "stopped"])}, - {"name": "nginx", "status": random.choice(["running", "running", "stopped"])}, - {"name": "sshd", "status": "running"}, - {"name": "ph1", "status": "running"}, - {"name": "ph2", "status": "stopped"}, - ] +def _get_docker_services() -> list[dict]: + """Query Docker for real container statuses with ternary status model.""" + try: + result = subprocess.run( + ["docker", "ps", "-a", "--format", "{{.Names}}\t{{.Status}}"], + capture_output=True, text=True, timeout=5, + ) + except (subprocess.TimeoutExpired, FileNotFoundError, OSError): + return [] + + if result.returncode != 0: + return [] + + services = [] + for line in result.stdout.strip().splitlines(): + parts = line.split("\t", 1) + if len(parts) != 2: + continue + name, raw_status = parts + + if raw_status.startswith("Up"): + if "unhealthy" in raw_status or "Restarting" in raw_status: + status = "warning" + else: + status = "running" + else: + status = "stopped" + + services.append({"name": name, "status": status}) + + # Sort: warnings first, then stopped, then running (problems float to top) + order = {"warning": 0, "stopped": 1, "running": 2} + services.sort(key=lambda s: order.get(s["status"], 3)) + + return services def _local_time_fields() -> dict: @@ -100,7 +125,7 @@ def generate_stats() -> dict: "uptime_hrs": round((time.time() - psutil.boot_time()) / 3600, 1), "net_rx_kbps": rx_kbps / 8, "net_tx_kbps": tx_kbps / 8, # kByte/s for humans - "services": _mock_services(), + "services": _get_docker_services(), "timestamp": int(time.time()), "local_time": _local_time_fields(), }