Files
knowledge-base/scripts/kb-audit.py
dttb a379b626af kb-audit: уровень 3 — auto-apply safe drift fixes (karpathy-style)
- kb_audit_helpers.py — общие функции parse_live/inventory/deleted
- kb-audit-apply.py — применяет только structural факт-правки:
  * new VMID → добавить в "🔴 Остановленные" (только для stopped)
  * missing VMID → переместить в "🗑️ Удалённые" с датой
- Коммитит как kb-audit-bot <kb-audit@dttb.ru> — фильтруемо в git log
- Safety: live<5 хостов → abort
- Не трогает описания/IP/назначения — только структурные поля из pct list

Cron обновлён: audit → apply → propose (остаток для ручного ревью)
2026-04-18 00:42:49 +03:00

106 lines
3.9 KiB
Python
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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()