diff --git a/docs/audit/preuve-2026-07-20.md b/docs/audit/preuve-2026-07-20.md index 1e6e42e..2b55e78 100644 --- a/docs/audit/preuve-2026-07-20.md +++ b/docs/audit/preuve-2026-07-20.md @@ -7,7 +7,7 @@ > [`docs/audit/affirmations.md`](affirmations.md). - **Instance** : `instance` — inventaire `instance/inventories/principal/hosts.yml` -- **Verdict** : ✅ CONFORME (15 OK · 0 echec · 1 saute) +- **Verdict** : ✅ CONFORME (16 OK · 0 echec · 0 saute) ## Preuves @@ -28,7 +28,7 @@ | P13 | Invariants structurels/doctrinaux | AFF-015, AFF-022, AFF-037, AFF-038, AFF-062 | ✅ OK | LICENSE, socle dossier, pas de couches paralleles, SSH clef-only, nftables off : OK. | | P14 | Pas de chemin lab/ code en dur | AFF-097 | ✅ OK | Aucun chemin instance/inventories/lab/group_vars code en dur. | | P15 | Modele public socle valide | AFF-022, AFF-099 | ✅ OK | Modele public socle : domaines/serveurs/applications/bases valides. | -| P16 | Inventaire Ansible complet (--list) | AFF-030 | ⚪ SAUTE | Voute chiffree sans ANSIBLE_VAULT_PASSWORD_FILE (prerequis AFF-026). | +| P16 | Inventaire Ansible complet (--list) | AFF-030 | ✅ OK | 14 hotes, 29 groupes (inventaire dechiffre et parse). | ## Couverture des affirmations ✅ du registre diff --git a/scripts/prouver.py b/scripts/prouver.py index 4aae936..2b82874 100644 --- a/scripts/prouver.py +++ b/scripts/prouver.py @@ -20,6 +20,7 @@ Usage : from __future__ import annotations import datetime as _dt +import json import os import subprocess import sys @@ -178,6 +179,22 @@ def preuve_pas_lab_code_en_dur() -> tuple[bool, str]: return True, "Aucun chemin instance/inventories/lab/group_vars code en dur." +def preuve_inventaire_ansible() -> tuple[bool, str]: + """`ansible-inventory --list` : l'inventaire (voute dechiffree) se parse (AFF-030).""" + r = subprocess.run(["ansible-inventory", "-i", INV, "--list"], + cwd=RACINE, capture_output=True, text=True, timeout=600) + if r.returncode != 0: + sortie = (r.stdout + r.stderr).strip().splitlines() + return False, f"rc={r.returncode} : {sortie[-1] if sortie else ''}" + try: + data = json.loads(r.stdout) + except ValueError: + return True, "inventaire liste (JSON non parse)." + n_hotes = len((data.get("_meta") or {}).get("hostvars") or {}) + n_groupes = len([k for k in data if k not in ("_meta", "all")]) + return True, f"{n_hotes} hotes, {n_groupes} groupes (inventaire dechiffre et parse)." + + # --- Registre des preuves : (id, titre, refs AFF, executeur) ----------------------- # # executeur = liste de commandes argv (toutes doivent renvoyer 0), ou callable -> (ok, detail). @@ -264,8 +281,7 @@ def main() -> int: # Preuve conditionnelle : inventaire Ansible complet (necessite la voute). preuve_inv = {"id": "P16", "titre": "Inventaire Ansible complet (--list)", - "refs": ["AFF-030"], - "cmds": [["ansible-inventory", "-i", INV, "--list"]]} + "refs": ["AFF-030"], "func": preuve_inventaire_ansible} liste = list(PREUVES) if _vault_requis_absent(): resultats.append((preuve_inv, "SAUTE",