#!/usr/bin/env python3 """Valide TOUS les modeles d'instance, pas seulement le socle public. Un modele est un dossier portant un `plan/` (serveurs, applications, bases, domaines, nomenclature). Ce script rejoue sur chacun les MEMES validateurs que sur une instance reelle — il ne reimplemente aucune regle. Pourquoi : les modeles assembles vivent dans un depot separe (actif commercial). Rien ne les verifiait, et `integral` a porte pendant des semaines une application posee sur un hote jamais declare. Un modele defectueux se propage a chaque locataire cree depuis lui. Decouverte des modeles : - `exemples/modeles/*` du depot (le socle public) ; - plus les chemins de la variable d'environnement SETOPS_MODELES (separes par ':'). Un chemin peut designer un modele OU un dossier de modeles. Absent = ignore en silence (le depot prive n'est pas toujours la). Usage : python3 scripts/modeles.py lister python3 scripts/modeles.py verifier SETOPS_MODELES=../Set-OPS-Modeles python3 scripts/modeles.py verifier """ from __future__ import annotations import argparse import os import subprocess import sys from pathlib import Path RACINE = Path(__file__).resolve().parents[1] MODELES_DEPOT = RACINE / "exemples" / "modeles" VALIDATEURS = ("domaines", "serveurs", "applications", "bases_donnees") def _est_modele(p: Path) -> bool: return p.is_dir() and (p / "plan").is_dir() def decouvrir() -> list[Path]: """Modeles du depot + ceux designes par SETOPS_MODELES. Tries, dedoublonnes.""" trouves: list[Path] = [] if MODELES_DEPOT.is_dir(): trouves += [p for p in sorted(MODELES_DEPOT.iterdir()) if _est_modele(p)] for brut in (os.environ.get("SETOPS_MODELES") or "").split(":"): brut = brut.strip() if not brut: continue chemin = Path(brut) if not chemin.is_absolute(): chemin = (RACINE / chemin).resolve() if not chemin.is_dir(): continue if _est_modele(chemin): trouves.append(chemin) else: trouves += [p for p in sorted(chemin.iterdir()) if _est_modele(p)] vus: dict[str, Path] = {} for p in trouves: vus.setdefault(str(p.resolve()), p) return list(vus.values()) def verifier_un(modele: Path) -> list[str]: """Rejoue les 4 validateurs sur un modele. Renvoie la liste des erreurs.""" erreurs: list[str] = [] env = {**os.environ, "SETOPS_INSTANCE": str(modele)} env.pop("SETOPS_INVENTAIRE", None) for nom in VALIDATEURS: r = subprocess.run([sys.executable, f"scripts/{nom}.py", "verifier"], cwd=RACINE, env=env, capture_output=True, text=True) if r.returncode != 0: lignes = (r.stdout + r.stderr).strip().splitlines() erreurs.append(f"{nom}: {lignes[-1] if lignes else 'rc!=0'}") return erreurs def main() -> int: parser = argparse.ArgumentParser(description="Validation des modeles d'instance Set-OPS.") sub = parser.add_subparsers(dest="commande", required=True) sub.add_parser("lister", help="Liste les modeles decouverts.") sub.add_parser("verifier", help="Valide les registres de chaque modele.") args = parser.parse_args() modeles = decouvrir() if args.commande == "lister": if not modeles: print("Aucun modele decouvert.") return 0 for m in modeles: try: rel = m.relative_to(RACINE) except ValueError: rel = m print(f"{m.name} ({rel})") return 0 if not modeles: print("Aucun modele decouvert : rien a verifier.") return 0 en_faute = 0 for m in modeles: erreurs = verifier_un(m) if erreurs: en_faute += 1 print(f"MODELE {m.name} : INVALIDE", file=sys.stderr) for e in erreurs: print(f" - {e}", file=sys.stderr) else: print(f"MODELE {m.name} : valide") if en_faute: print(f"erreur: {en_faute} modele(s) invalide(s) sur {len(modeles)}.", file=sys.stderr) return 2 print(f"Les {len(modeles)} modele(s) decouvert(s) valident.") return 0 if __name__ == "__main__": raise SystemExit(main())