Add system info
This commit is contained in:
73
app.py
73
app.py
@@ -48,6 +48,68 @@ def docker_stats() -> dict:
|
||||
}
|
||||
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:
|
||||
# RestartCount is useful when stuff is flapping / OOMing
|
||||
try:
|
||||
@@ -127,10 +189,21 @@ def collect():
|
||||
if a["restarts"] >= 3:
|
||||
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 {
|
||||
"generated_at": datetime.utcnow().isoformat() + "Z",
|
||||
"poll_seconds": POLL_SECONDS,
|
||||
"domain": APP_DOMAIN,
|
||||
"system": sysinfo,
|
||||
"apps": apps,
|
||||
"infra": infra,
|
||||
"warnings": warnings,
|
||||
|
||||
@@ -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>
|
||||
|
||||
{% if data.warnings %}
|
||||
|
||||
Reference in New Issue
Block a user