diff options
Diffstat (limited to 'hetzner-admin/app.py')
| -rw-r--r-- | hetzner-admin/app.py | 44 |
1 files changed, 44 insertions, 0 deletions
diff --git a/hetzner-admin/app.py b/hetzner-admin/app.py index a9377a8..17895d9 100644 --- a/hetzner-admin/app.py +++ b/hetzner-admin/app.py @@ -55,6 +55,7 @@ app.config.update( COMMANDS = [ ("Docker Status", "docker ps"), + ("Container Stats", "docker stats"), ("Restart Caddy", "docker compose -f /app/infra/caddy/docker-compose.yml restart"), ("Restart Mail", "docker compose -f /app/infra/mail/docker-compose.yml restart"), ("Restart cgit", "docker compose -f /app/infra/cgit/docker-compose.yml restart"), @@ -110,6 +111,20 @@ HTML = """<!DOCTYPE html> <body> <h3>Server Admin</h3> <p><a href="https://status.yyamashita.com">Status</a></p> +<h4>Resources</h4> +{% if resources is string %} +<p style="color:red">{{ resources }}</p> +{% else %} +<table> +{% for label, pct, text in resources %} +<tr> + <td>{{ label }}</td> + <td>{% if pct is not none %}<meter min="0" max="100" low="70" high="90" optimum="0" value="{{ pct }}"></meter>{% endif %}</td> + <td>{{ text }}</td> +</tr> +{% endfor %} +</table> +{% endif %} <h4>Services</h4> {% if services is string %} <p style="color:red">{{ services }}</p> @@ -214,10 +229,39 @@ def load_services(): return [(name, states.get(name, "unknown")) for name in SERVICES] +def fmt_bytes(n): + for unit in ("B", "KiB", "MiB", "GiB", "TiB"): + if n < 1024 or unit == "TiB": + return f"{n:.1f} {unit}" + n /= 1024 + + +def load_resources(): + """[(label, percent|None, text), ...]。取得できなければエラーメッセージ(str)""" + try: + out = run_ssh("resources") + v = {k: rest.split() for k, _, rest in (line.partition(" ") for line in out.splitlines())} + cpus = int(v["cpus"][0]) + load = [float(x) for x in v["load"]] + mem_total, mem_used = (int(x) for x in v["mem"]) + disk_total, disk_used = (int(x) for x in v["disk"]) + up = int(v["uptime"][0]) + except Exception as e: + return f"Error loading resources: {e}" + return [ + ("CPU", min(100, load[0] / cpus * 100), + f"load {' / '.join(f'{x:.2f}' for x in load)} ({cpus} cores)"), + ("Memory", mem_used / mem_total * 100, f"{fmt_bytes(mem_used)} / {fmt_bytes(mem_total)}"), + ("Disk /", disk_used / disk_total * 100, f"{fmt_bytes(disk_used)} / {fmt_bytes(disk_total)}"), + ("Uptime", None, f"{up // 86400}d {up % 86400 // 3600}h {up % 3600 // 60}m"), + ] + + def render_index(output=None): return render_template_string( HTML, commands=COMMANDS, + resources=load_resources(), services=load_services(), output=output, passkey_count=len(load_passkeys()), |
