#!/usr/bin/env python3 """ kb-audit — детектит drift между живой инфраструктурой и KB-инвентарями. Пишет структурированный отчёт в audit/YYYY-MM-DD-drift.md. Только факты — никаких LLM, галлюцинаций быть не может. Запускать из code-server (LXC 132) или любой машины с sshpass + доступом к Proxmox. """ import sys from datetime import date from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent)) from kb_audit_helpers import parse_live, parse_inventory, find_deleted_section, INVENTORY, VAULT # type: ignore OUT_DIR = VAULT / "audit" def compare(live: dict, inventory: dict): live_ids = set(live.keys()) inv_ids = set(inventory.keys()) only_live = sorted(live_ids - inv_ids, key=int) only_inv = sorted(inv_ids - live_ids, key=int) both = sorted(live_ids & inv_ids, key=int) return only_live, only_inv, both def main(): today = date.today().isoformat() OUT_DIR.mkdir(parents=True, exist_ok=True) out = OUT_DIR / f"{today}-drift.md" live = parse_live() inventory = parse_inventory(INVENTORY) deleted = find_deleted_section(INVENTORY) only_live, only_inv_raw, common = compare(live, inventory) only_inv = [v for v in only_inv_raw if v not in deleted] known_deleted = [v for v in only_inv_raw if v in deleted] lines = [ "---", f"date: {today}", "type: audit", "source: kb-audit.py", "tags: [audit, drift, infrastructure]", "---", "", f"# KB drift audit — {today}", "", "Сравнение живого `pct list` / `qm list` с [[../projects/dttb/proxmox-inventory|proxmox-inventory.md]]", "", f"- Живых гостей Proxmox: **{len(live)}**", f"- Упомянуто в inventory: **{len(inventory)}**", f"- В обоих: {len(common)} / только в live: {len(only_live)} / отсутствуют в live: {len(only_inv)}", f"- Известны как удалённые: {len(known_deleted)} (в `## 🗑️ Удалённые`)", "", ] if only_live: lines += ["## ⚠ В Proxmox есть, в inventory НЕТ (надо добавить)", ""] lines += ["| VMID | Type | Status | Name |", "|---|---|---|---|"] for vmid in only_live: x = live[vmid] lines += [f"| {vmid} | {x['type']} | {x['status']} | {x['name']} |"] lines += [""] if only_inv: lines += ["## ❓ В inventory есть, в Proxmox НЕТ (не в секции 🗑️ — проверить вручную)", ""] lines += ["| VMID | Контекст из inventory |", "|---|---|"] for vmid in only_inv: ctx = inventory[vmid][:100] lines += [f"| {vmid} | `...{ctx}...` |"] lines += [""] if known_deleted: lines += [f"## ✓ Удалённые хосты (задокументированы): {', '.join(sorted(known_deleted, key=int))}", ""] if not only_live and not only_inv: lines += ["## ✓ Inventory полностью совпадает с живой инфраструктурой", ""] lines += [ "## Полный живой список", "", "| VMID | Type | Status | Name |", "|---|---|---|---|", ] for vmid in sorted(live.keys(), key=int): x = live[vmid] lines += [f"| {vmid} | {x['type']} | {x['status']} | {x['name']} |"] lines += [ "", "---", "*Автоматически сгенерировано `scripts/kb-audit.py`. Применять правки — вручную после ревью.*", ] out.write_text("\n".join(lines)) print(f"drift report: {out}") print(f" only_live: {len(only_live)}") print(f" only_inv: {len(only_inv)}") if __name__ == "__main__": main()