Add system info

This commit is contained in:
Peter Stockings
2025-12-22 13:06:15 +11:00
parent 147f38dfb4
commit 2f75b9669b
2 changed files with 120 additions and 0 deletions

73
app.py
View File

@@ -48,6 +48,68 @@ def docker_stats() -> dict:
} }
return stats return stats
import re
def docker_info() -> dict:
# docker info --format "{{json .}}" gives us structured host-level info
out = sh([DOCKER, "info", "--format", "{{json .}}"])
return json.loads(out)
def docker_system_df() -> dict:
# Parse `docker system df` (text). It's stable enough for a dashboard.
out = sh([DOCKER, "system", "df"])
# Example lines:
# Images 175 18 15.15GB 13.93GB (91%)
# Containers 27 26 145.1MB 16.57kB (0%)
# Local Volumes 47 1 817.7MB 817.7MB (100%)
# Build Cache 889 0 423B 423B
rows = {}
for line in out.splitlines():
line = line.strip()
if not line or line.startswith("TYPE"):
continue
parts = re.split(r"\s{2,}", line)
if len(parts) >= 5:
typ, total, active, size, reclaimable = parts[:5]
rows[typ] = {
"total": total,
"active": active,
"size": size,
"reclaimable": reclaimable,
}
return rows
def system_summary() -> dict:
info = docker_info()
df = docker_system_df()
return {
"name": info.get("Name", ""),
"server_version": info.get("ServerVersion", ""),
"operating_system": info.get("OperatingSystem", ""),
"os_type": info.get("OSType", ""),
"architecture": info.get("Architecture", ""),
"kernel_version": info.get("KernelVersion", ""),
"cpus": info.get("NCPU", ""),
"mem_total": info.get("MemTotal", ""), # bytes
"containers": info.get("Containers", ""),
"containers_running": info.get("ContainersRunning", ""),
"containers_stopped": info.get("ContainersStopped", ""),
"images": info.get("Images", ""),
"docker_root_dir": info.get("DockerRootDir", ""),
"system_df": df,
}
def format_bytes(n: int) -> str:
# for mem_total
units = ["B", "KB", "MB", "GB", "TB"]
f = float(n)
for u in units:
if f < 1024 or u == units[-1]:
return f"{f:.1f}{u}"
f /= 1024
return f"{n}B"
def docker_inspect_restart_count(container_name: str) -> int: def docker_inspect_restart_count(container_name: str) -> int:
# RestartCount is useful when stuff is flapping / OOMing # RestartCount is useful when stuff is flapping / OOMing
try: try:
@@ -126,11 +188,22 @@ def collect():
warnings.append(f"{a['app']} RAM high ({a['mem_pct']})") warnings.append(f"{a['app']} RAM high ({a['mem_pct']})")
if a["restarts"] >= 3: if a["restarts"] >= 3:
warnings.append(f"{a['app']} restarting (restarts={a['restarts']})") warnings.append(f"{a['app']} restarting (restarts={a['restarts']})")
sysinfo = system_summary()
# format mem bytes nicely
try:
mem_total_h = format_bytes(int(sysinfo["mem_total"]))
except Exception:
mem_total_h = ""
sysinfo["mem_total_h"] = mem_total_h
return { return {
"generated_at": datetime.utcnow().isoformat() + "Z", "generated_at": datetime.utcnow().isoformat() + "Z",
"poll_seconds": POLL_SECONDS, "poll_seconds": POLL_SECONDS,
"domain": APP_DOMAIN, "domain": APP_DOMAIN,
"system": sysinfo,
"apps": apps, "apps": apps,
"infra": infra, "infra": infra,
"warnings": warnings, "warnings": warnings,

View File

@@ -1,3 +1,50 @@
<h2>System</h2>
<div style="display:grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; margin: 12px 0 18px 0;">
<div style="border:1px solid #ddd; border-radius: 10px; padding: 12px;">
<div class="muted">Host</div>
<div><strong>{{ data.system.name or "—" }}</strong></div>
<div class="muted">{{ data.system.operating_system }} · {{ data.system.kernel_version }}</div>
</div>
<div style="border:1px solid #ddd; border-radius: 10px; padding: 12px;">
<div class="muted">Compute</div>
<div><strong>{{ data.system.cpus or "—" }}</strong> CPUs</div>
<div><strong>{{ data.system.mem_total_h or "—" }}</strong> RAM</div>
</div>
<div style="border:1px solid #ddd; border-radius: 10px; padding: 12px;">
<div class="muted">Docker</div>
<div>Engine: <strong>{{ data.system.server_version or "—" }}</strong></div>
<div>Images: <strong>{{ data.system.images or "—" }}</strong></div>
<div>Containers: <strong>{{ data.system.containers_running or "—" }}</strong> running / <strong>{{
data.system.containers_stopped or "—" }}</strong> stopped</div>
</div>
</div>
<h3>Docker disk usage</h3>
<table>
<thead>
<tr>
<th>Type</th>
<th>Total</th>
<th>Active</th>
<th>Size</th>
<th>Reclaimable</th>
</tr>
</thead>
<tbody>
{% for typ, r in data.system.system_df.items() %}
<tr>
<td><strong>{{ typ }}</strong></td>
<td>{{ r.total }}</td>
<td>{{ r.active }}</td>
<td>{{ r.size }}</td>
<td>{{ r.reclaimable }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="muted">Generated at: {{ data.generated_at }}</div> <div class="muted">Generated at: {{ data.generated_at }}</div>
{% if data.warnings %} {% if data.warnings %}