Add backup job controls and systemd status

This commit is contained in:
2026-02-08 00:48:20 +03:00
parent c89e7259f6
commit be65398b86
2 changed files with 83 additions and 1 deletions

View File

@@ -12,6 +12,59 @@ from services.backup import backup_badge, restore_help
from services.runner import run_cmd
def _parse_systemctl_kv(raw: str) -> dict[str, str]:
data: dict[str, str] = {}
for line in raw.splitlines():
if "=" not in line:
continue
key, value = line.split("=", 1)
data[key.strip()] = value.strip()
return data
async def _unit_status(unit: str, props: list[str]) -> dict[str, str]:
args = ["systemctl", "show", unit] + [f"-p{prop}" for prop in props]
rc, out = await run_cmd(args, timeout=10)
if rc != 0:
return {"error": out.strip() or f"systemctl {unit} failed"}
return _parse_systemctl_kv(out)
async def send_backup_jobs_status(msg: Message):
services = [
("backup-auto", "backup-auto.timer"),
("restic-check", "restic-check.timer"),
("weekly-report", "weekly-report.timer"),
]
service_props = ["ActiveState", "SubState", "Result", "ExecMainStatus", "ExecMainExitTimestamp"]
timer_props = ["LastTriggerUSecRealtime", "NextElapseUSecRealtime"]
lines = ["🕒 Backup jobs\n"]
for service, timer in services:
svc = await _unit_status(f"{service}.service", service_props)
tmr = await _unit_status(timer, timer_props)
if "error" in svc:
lines.append(f"🔴 {service}: {svc['error']}")
continue
active = svc.get("ActiveState", "n/a")
result = svc.get("Result", "n/a")
exit_status = svc.get("ExecMainStatus", "n/a")
last = svc.get("ExecMainExitTimestamp", "n/a")
next_run = tmr.get("NextElapseUSecRealtime", "n/a")
last_trigger = tmr.get("LastTriggerUSecRealtime", "n/a")
lines.append(
f"🧊 {service}: {active} ({result}, rc={exit_status})"
)
lines.append(f" Last run: {last}")
lines.append(f" Last trigger: {last_trigger}")
lines.append(f" Next: {next_run}")
lines.append("")
await msg.answer("\n".join(lines).rstrip(), reply_markup=backup_kb)
async def cmd_repo_stats(msg: Message):
await msg.answer("⏳ Loading repo stats…", reply_markup=backup_kb)
@@ -104,6 +157,7 @@ async def cmd_backup_status(msg: Message):
f"📦 Snapshots ({len(snaps)})\n{badge}",
reply_markup=kb
)
await send_backup_jobs_status(msg)
asyncio.create_task(worker())
@@ -202,6 +256,34 @@ async def br(msg: Message):
await cmd_backup_now(msg)
@dp.message(F.text == "🧪 Restic check")
async def rc(msg: Message):
if not is_admin_msg(msg):
return
async def job():
await msg.answer("🧪 Restic check запущен", reply_markup=backup_kb)
rc2, out = await run_cmd(["sudo", "/usr/local/bin/restic-check.sh"], timeout=6 * 3600)
await msg.answer(("✅ OK\n" if rc2 == 0 else "❌ FAIL\n") + out, reply_markup=backup_kb)
pos = await enqueue("restic-check", job)
await msg.answer(f"🕓 Restic check queued (#{pos})", reply_markup=backup_kb)
@dp.message(F.text == "📬 Weekly report")
async def wr(msg: Message):
if not is_admin_msg(msg):
return
async def job():
await msg.answer("📬 Weekly report запущен", reply_markup=backup_kb)
rc2, out = await run_cmd(["sudo", "/usr/local/bin/weekly-report.sh"], timeout=3600)
await msg.answer(("✅ OK\n" if rc2 == 0 else "❌ FAIL\n") + out, reply_markup=backup_kb)
pos = await enqueue("weekly-report", job)
await msg.answer(f"🕓 Weekly report queued (#{pos})", reply_markup=backup_kb)
@dp.message(F.text == "🧯 Restore help")
async def rh(msg: Message):
if is_admin_msg(msg):

View File

@@ -38,7 +38,7 @@ backup_kb = ReplyKeyboardMarkup(
[KeyboardButton(text="📦 Status"), KeyboardButton(text="📦 Last snapshot")],
[KeyboardButton(text="📊 Repo stats"), KeyboardButton(text="🧯 Restore help")],
[KeyboardButton(text="▶️ Run backup"), KeyboardButton(text="🧾 Queue")],
[KeyboardButton(text="⬅️ Назад")],
[KeyboardButton(text="🧪 Restic check"), KeyboardButton(text="📬 Weekly report"), KeyboardButton(text="⬅️ Назад")],
],
resize_keyboard=True,
)