Fix mojibake text and add md RAID checks
This commit is contained in:
@@ -67,7 +67,7 @@ async def snapshot_details(cb: CallbackQuery):
|
|||||||
snap_id = cb.data.split(":", 1)[1]
|
snap_id = cb.data.split(":", 1)[1]
|
||||||
await cb.answer("Loading snapshot…")
|
await cb.answer("Loading snapshot…")
|
||||||
|
|
||||||
# получаем статистику snapshot
|
# получаем статистику snapshot
|
||||||
rc, raw = await run_cmd(
|
rc, raw = await run_cmd(
|
||||||
["restic", "stats", snap_id, "--json"],
|
["restic", "stats", snap_id, "--json"],
|
||||||
use_restic_env=True,
|
use_restic_env=True,
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ def _npm_api_base(cfg) -> str | None:
|
|||||||
|
|
||||||
|
|
||||||
def health(cfg, container_map: dict | None = None) -> str:
|
def health(cfg, container_map: dict | None = None) -> str:
|
||||||
lines = ["рџ©є Health check\n"]
|
lines = ["🩺 Health check\n"]
|
||||||
thresholds = cfg.get("thresholds", {})
|
thresholds = cfg.get("thresholds", {})
|
||||||
disk_warn = int(thresholds.get("disk_warn", 80))
|
disk_warn = int(thresholds.get("disk_warn", 80))
|
||||||
load_warn = float(thresholds.get("load_warn", 2.0))
|
load_warn = float(thresholds.get("load_warn", 2.0))
|
||||||
@@ -45,9 +45,9 @@ def health(cfg, container_map: dict | None = None) -> str:
|
|||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
env.update(RESTIC_ENV)
|
env.update(RESTIC_ENV)
|
||||||
subprocess.check_output(["restic", "snapshots"], timeout=10, env=env)
|
subprocess.check_output(["restic", "snapshots"], timeout=10, env=env)
|
||||||
lines.append("рџџў Backup repo reachable")
|
lines.append("🟢 Backup repo reachable")
|
||||||
except Exception:
|
except Exception:
|
||||||
lines.append("🔴 Backup repo unreachable")
|
lines.append("🔴 Backup repo unreachable")
|
||||||
|
|
||||||
containers = container_map if container_map is not None else _containers_from_cfg(cfg)
|
containers = container_map if container_map is not None else _containers_from_cfg(cfg)
|
||||||
for alias, real in containers.items():
|
for alias, real in containers.items():
|
||||||
@@ -55,20 +55,20 @@ def health(cfg, container_map: dict | None = None) -> str:
|
|||||||
f"docker inspect -f '{{{{.State.Status}}}}' {real}"
|
f"docker inspect -f '{{{{.State.Status}}}}' {real}"
|
||||||
)
|
)
|
||||||
if out.strip() != "running":
|
if out.strip() != "running":
|
||||||
lines.append(f"🔴 {alias} down")
|
lines.append(f"🔴 {alias} down")
|
||||||
else:
|
else:
|
||||||
lines.append(f"рџџў {alias} OK")
|
lines.append(f"🟢 {alias} OK")
|
||||||
|
|
||||||
npm_cfg = cfg.get("npmplus", {})
|
npm_cfg = cfg.get("npmplus", {})
|
||||||
npm_base = _npm_api_base(cfg)
|
npm_base = _npm_api_base(cfg)
|
||||||
if npm_base:
|
if npm_base:
|
||||||
npm_status = _request_status(npm_base, npm_cfg.get("verify_tls", True))
|
npm_status = _request_status(npm_base, npm_cfg.get("verify_tls", True))
|
||||||
if npm_status == 200:
|
if npm_status == 200:
|
||||||
lines.append("рџџў NPMplus API OK")
|
lines.append("🟢 NPMplus API OK")
|
||||||
elif npm_status is None:
|
elif npm_status is None:
|
||||||
lines.append("🔴 NPMplus API unreachable")
|
lines.append("🔴 NPMplus API unreachable")
|
||||||
else:
|
else:
|
||||||
lines.append(f"рџџЎ NPMplus API HTTP {npm_status}")
|
lines.append(f"🟡 NPMplus API HTTP {npm_status}")
|
||||||
|
|
||||||
g_cfg = cfg.get("gitea", {})
|
g_cfg = cfg.get("gitea", {})
|
||||||
g_base = (g_cfg.get("base_url") or "").rstrip("/")
|
g_base = (g_cfg.get("base_url") or "").rstrip("/")
|
||||||
@@ -84,22 +84,22 @@ def health(cfg, container_map: dict | None = None) -> str:
|
|||||||
g_status = status
|
g_status = status
|
||||||
break
|
break
|
||||||
if g_status == 200:
|
if g_status == 200:
|
||||||
lines.append("рџџў Gitea API OK")
|
lines.append("🟢 Gitea API OK")
|
||||||
elif g_status is None:
|
elif g_status is None:
|
||||||
lines.append("🔴 Gitea API unreachable")
|
lines.append("🔴 Gitea API unreachable")
|
||||||
else:
|
else:
|
||||||
lines.append(f"рџџЎ Gitea API HTTP {g_status}")
|
lines.append(f"🟡 Gitea API HTTP {g_status}")
|
||||||
|
|
||||||
usage, mount = worst_disk_usage()
|
usage, mount = worst_disk_usage()
|
||||||
if usage is None:
|
if usage is None:
|
||||||
lines.append("вљ пёЏ Disk n/a")
|
lines.append("⚠️ Disk n/a")
|
||||||
elif usage > disk_warn:
|
elif usage > disk_warn:
|
||||||
lines.append(f"рџџЎ Disk {usage}% ({mount})")
|
lines.append(f"🟡 Disk {usage}% ({mount})")
|
||||||
else:
|
else:
|
||||||
lines.append(f"рџџў Disk {usage}% ({mount})")
|
lines.append(f"🟢 Disk {usage}% ({mount})")
|
||||||
|
|
||||||
load = psutil.getloadavg()[0]
|
load = psutil.getloadavg()[0]
|
||||||
lines.append(f"{'рџџў' if load < load_warn else 'рџџЎ'} Load {load}")
|
lines.append(f"{'🟢' if load < load_warn else '🟡'} Load {load}")
|
||||||
|
|
||||||
return "\n".join(lines)
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|||||||
@@ -82,6 +82,53 @@ def list_disks() -> list[str]:
|
|||||||
return disks
|
return disks
|
||||||
|
|
||||||
|
|
||||||
|
def list_md_arrays() -> list[str]:
|
||||||
|
out = _cmd("lsblk -dn -o NAME,TYPE")
|
||||||
|
arrays = []
|
||||||
|
for line in out.splitlines():
|
||||||
|
parts = line.split()
|
||||||
|
if len(parts) != 2:
|
||||||
|
continue
|
||||||
|
name, typ = parts
|
||||||
|
if typ == "raid" and name.startswith("md"):
|
||||||
|
arrays.append(f"/dev/{name}")
|
||||||
|
return arrays
|
||||||
|
|
||||||
|
|
||||||
|
def md_array_status(dev: str) -> str:
|
||||||
|
out = _cmd("cat /proc/mdstat")
|
||||||
|
if not out or "ERROR:" in out:
|
||||||
|
return "⚠️ n/a"
|
||||||
|
|
||||||
|
name = dev.rsplit("/", 1)[-1]
|
||||||
|
lines = out.splitlines()
|
||||||
|
header = None
|
||||||
|
idx = -1
|
||||||
|
for i, line in enumerate(lines):
|
||||||
|
s = line.strip()
|
||||||
|
if s.startswith(f"{name} :"):
|
||||||
|
header = s
|
||||||
|
idx = i
|
||||||
|
break
|
||||||
|
|
||||||
|
if not header:
|
||||||
|
return "⚠️ not found in /proc/mdstat"
|
||||||
|
|
||||||
|
if "inactive" in header:
|
||||||
|
return "🔴 inactive"
|
||||||
|
|
||||||
|
# Typical mdstat health marker: [UU] for healthy mirrors/raid members.
|
||||||
|
block = [header]
|
||||||
|
for line in lines[idx + 1:]:
|
||||||
|
if not line.strip():
|
||||||
|
break
|
||||||
|
block.append(line.strip())
|
||||||
|
block_text = " ".join(block)
|
||||||
|
if "[U_" in block_text or "[_U" in block_text:
|
||||||
|
return "🟡 degraded"
|
||||||
|
return "🟢 active"
|
||||||
|
|
||||||
|
|
||||||
def smart_health(dev: str) -> str:
|
def smart_health(dev: str) -> str:
|
||||||
out = _cmd(f"smartctl -H {dev}")
|
out = _cmd(f"smartctl -H {dev}")
|
||||||
|
|
||||||
@@ -138,8 +185,9 @@ def smart_last_test(dev: str) -> str:
|
|||||||
|
|
||||||
def disks() -> str:
|
def disks() -> str:
|
||||||
disks = list_disks()
|
disks = list_disks()
|
||||||
|
md_arrays = list_md_arrays()
|
||||||
|
|
||||||
if not disks:
|
if not disks and not md_arrays:
|
||||||
return "💽 Disks\n\n❌ No disks found"
|
return "💽 Disks\n\n❌ No disks found"
|
||||||
|
|
||||||
lines = ["💽 Disks (SMART)\n"]
|
lines = ["💽 Disks (SMART)\n"]
|
||||||
@@ -158,6 +206,12 @@ def disks() -> str:
|
|||||||
|
|
||||||
lines.append(f"{icon} {d} — {health}, 🌡 {temp}")
|
lines.append(f"{icon} {d} — {health}, 🌡 {temp}")
|
||||||
|
|
||||||
|
if md_arrays:
|
||||||
|
lines.append("")
|
||||||
|
lines.append("🧱 RAID (md)")
|
||||||
|
for md in md_arrays:
|
||||||
|
lines.append(f"{md} — {md_array_status(md)}")
|
||||||
|
|
||||||
return "\n".join(lines)
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user