#!/usr/bin/env python3 """Une sonde posee sous condition est declaree sous la MEME condition. UNE LISTE QUI EN SUIT UNE AUTRE PREND DU RETARD (2026-09-28). Un role pose sa sonde dans `/usr/local/lib/setops/sondes/` avec ses taches ; Icinga l'attend d'apres `meta/supervision.yml`. Quand le depot est conditionnel (`when:`) et la declaration ne l'est pas, Icinga attend une sonde qui n'existera jamais : `journaux-frontiere` a ete rouge a vie chez chaque locataire, dont la frontiere appartient au site. Trois regles : 1. toute sonde deposee sous `when:` est declaree avec `seulement_si` ; 2. la variable de `seulement_si` figure dans ce `when:` ; 3. elle a une valeur par defaut LITTERALE dans le role qui la definit — Icinga la lit telle quelle, il ne rend pas le Jinja d'un autre role. """ from __future__ import annotations import sys from pathlib import Path import yaml ROLES = Path(__file__).resolve().parents[2] / "roles" def depots_conditionnels() -> dict[str, str]: """{nom de sonde : texte du `when:`} pour chaque sonde deposee sous condition.""" out = {} for f in sorted(ROLES.glob("*/tasks/*.yml")): try: taches = yaml.safe_load(f.read_text(encoding="utf-8")) or [] except yaml.YAMLError: continue for t in taches if isinstance(taches, list) else []: if not isinstance(t, dict): continue module = next((v for v in t.values() if isinstance(v, dict) and "dest" in v), None) dest = str((module or {}).get("dest", "")) if "/setops/sondes/" in dest and t.get("when"): quand = t["when"] if isinstance(t["when"], str) else " ".join(map(str, t["when"])) out[dest.rsplit("/", 1)[-1].removesuffix(".sh")] = quand return out def declarations() -> dict[str, tuple[str, dict]]: """{nom de sonde : (role declarant, declaration)}.""" out = {} for f in sorted(ROLES.glob("*/meta/supervision.yml")): for s in (yaml.safe_load(f.read_text(encoding="utf-8")) or {}).get("sondes") or []: out[s["nom"]] = (f.parents[1].name, s) return out def main() -> int: depots, decl = depots_conditionnels(), declarations() echecs = 0 for nom, quand in sorted(depots.items()): role, s = decl.get(nom, (None, {})) var = s.get("seulement_si") if not var: print(f"ECHEC « {nom} » est posee sous `when: {quand}` mais declaree sans " f"`seulement_si` : Icinga l'attendrait la ou elle n'existe pas") echecs += 1 continue if var not in quand: print(f"ECHEC « {nom} » : `seulement_si: {var}` n'est pas la condition du depot " f"(`when: {quand}`)") echecs += 1 continue definisseur = s.get("defini_par") or role defauts = yaml.safe_load((ROLES / definisseur / "defaults" / "main.yml") .read_text(encoding="utf-8")) or {} val = defauts.get(var, "") if val == "" or (isinstance(val, str) and "{{" in val): print(f"ECHEC « {nom} » : `{var}` doit avoir un defaut LITTERAL dans " f"`{definisseur}/defaults/main.yml` (trouve : {val!r})") echecs += 1 continue print(f"ok {nom:<20} seulement_si {var} (defaut {val!r}, {definisseur})") for nom, (role, s) in sorted(decl.items()): if s.get("seulement_si") and nom not in depots: print(f"ECHEC « {nom} » ({role}) declare `seulement_si` sans depot conditionnel") echecs += 1 if echecs: print(f"\n{echecs} echec(s).") return 1 print("OK — chaque sonde conditionnelle est attendue sous sa propre condition.") return 0 if __name__ == "__main__": sys.exit(main())