Some checks failed
verifier / verifier (push) Has been cancelled
La revision a commence par un balayage par motifs — chemins morts, cibles make absentes, comptes derives. Il a trouve une trentaine d ecarts et rate presque tout le reste : un motif ne voit que ce qui s exprime en motif. make hote-planifier en est l exemple. La cible EXISTE, donc le controle passait au vert. C est une cible depreciee qui refuse et sort en 2, recommandee par AGENTS.md, et qui contredit la REGLE D OR du meme fichier trois ecrans plus haut. Il fallait lire pour la voir. 74 documents lus un par un. 66 corriges, 8 exacts. CE QUI ETAIT FRANCHEMENT FAUX AGENTS.md, la source d autorite, annoncait la flotte pas encore executee contre des VM reelles. Elle a ete rasee et remontee depuis zero trois fois. ecosysteme-chezlepro.md, le document montre a un client, portait la meme phrase : il se sous-vendait gravement. courriel-conception.md s ouvrait sur aucun role n est encore ecrit, au-dessus de son propre paragraphe 1 qui les nomme. autorisation.md se terminait sur rien n est construit alors qu il rapporte des mesures datees du role en fonctionnement. hebergeur-exploitation.md disait rien n est fait d un depot qui existe. filiation-emancipation.md se contredisait a deux ecrans de distance. DES MODELES DECRITS D APRES UN MONDE ANTERIEUR Le resolveur : cinq documents decrivaient un Unbound par VM en opt-in, trois le donnaient en exemple d integration FACULTATIVE — il est universel depuis le 2026-08-24. L adressage de nomenclature-vm.md : reseau unique, VLAN 11-15, VMID a cinq chiffres. Le nommage SDN de sdn-evpn.md contre le code : c est le wiki qui avait raison. CE QUI CASSE AU PREMIER ESSAI Le nom du gabarit dore etait faux a quatre endroits, dont la procedure qui le FABRIQUE et le critere R2 de l epreuve d operateur independant. preparer-un-site-hebergeur.md avertissait qu une VM faite a la main serait detruite : raser derive du plan, il ne la detruira jamais — le risque est l inverse. Un mot de passe d essai en clair dans un depot public. DEUX PREUVES ETENDUES, ET UNE QUI SE TROMPAIT ELLE-MEME P57 couvre les groupes : elle a signale aussitot 29 groupes annonces au-dessus d un tableau qui en cite 40. P29 confronte le tableau de authentification.md aux declarations reelles : 12 annonces, 21 reels. Et P57 imposait un chiffre faux — 56 preuves alors que le depot en porte 57, la conditionnelle vivant hors de tout comptage. Un garde-fou qui fait respecter une erreur ajoute l assurance a l erreur. CE QUI RESTE, ET QU AUCUNE PREUVE NE TIENT Deux comptes trouves a la main. Et une lacune reelle : rien ne garde les meta/acces.yml — ni qu un service web-sso en porte un, ni que le groupe qu il nomme existe. P29 tient les positions d authentification, personne ne tient les habilitations. make prouver : CONFORME, 56 OK, 0 echec, 1 saute. 0 lien mort. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
155 lines
6 KiB
Python
155 lines
6 KiB
Python
#!/usr/bin/env python3
|
|
"""Registre des serveurs (VM) du plan Set-OPS.
|
|
|
|
LE PLAN EST L'AUTORITE, ET CE N'EST PLUS L'INVERSE (corrige le 2026-09-06). Ce module a
|
|
longtemps annonce « Phase 2 : l'inventaire reste AUTORITE [...] la generation depuis le
|
|
plan viendra en Phase 3 ». La Phase 3 est faite depuis longtemps : `make instancier`
|
|
genere `hosts.yml` DEPUIS ce registre, et la REGLE D'OR d'AGENTS.md interdit d'editer
|
|
l'inventaire a la main. Lire l'ancienne phrase aujourd'hui, c'est croire l'exact contraire
|
|
de la doctrine.
|
|
|
|
`bootstrap` reste utile, mais c'est une manoeuvre de REPRISE, pas le flux normal : il
|
|
(re)constitue `plan/serveurs.yml` depuis un inventaire existant — le geste qu'on fait une
|
|
fois, quand on herite d'un ecosysteme dont le plan n'existe pas encore.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
|
|
import argparse
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import yaml
|
|
|
|
from inventory_gui import charger_yaml, liste_hotes
|
|
from inventory_rules import (
|
|
charger_nomenclature,
|
|
charger_serveurs,
|
|
fonction_seq,
|
|
integrations_universelles,
|
|
instance_courante,
|
|
inventaire_de,
|
|
reconcilier_serveur,
|
|
valider_serveurs,
|
|
)
|
|
|
|
RACINE = Path(__file__).resolve().parents[1]
|
|
INSTANCE = instance_courante()
|
|
FICHIER = INSTANCE / "plan/serveurs.yml"
|
|
FICHIER_NOMENCLATURE = INSTANCE / "plan/nomenclature.yml"
|
|
INVENTAIRE = inventaire_de(INSTANCE)
|
|
|
|
# Champs de placement / dimensionnement NON derivables (proviennent du plan).
|
|
CHAMPS_PLAN = [("noeud", "noeud"), ("stockage", "stockage"),
|
|
("disque_taille", "disque"), ("memoire", "memoire"), ("coeurs", "coeurs")]
|
|
|
|
|
|
def ecrire(registre: dict) -> None:
|
|
entete = (
|
|
"# Registre des serveurs (VM) du plan Set-OPS.\n"
|
|
"# CE FICHIER EST L'AUTORITE : l'inventaire en est GENERE (make instancier).\n"
|
|
"# Ne jamais editer instance/inventories/*/hosts.yml a la main.\n"
|
|
"# Reconstitue ici depuis un inventaire existant par `make serveurs-bootstrap` —\n"
|
|
"# manoeuvre de reprise, pas le flux normal.\n"
|
|
"# VMID/IP/VLAN/passerelle sont DERIVES de la fonction via\n"
|
|
"# instance/plan/nomenclature.yml (jamais stockes ici).\n"
|
|
"---\n"
|
|
)
|
|
with FICHIER.open("w", encoding="utf-8") as fichier:
|
|
fichier.write(entete)
|
|
yaml.safe_dump({"serveurs": registre.get("serveurs", {}) or {}},
|
|
fichier, default_flow_style=False, sort_keys=False, allow_unicode=True)
|
|
|
|
|
|
def hotes_inventaire() -> dict:
|
|
return {h["nom"]: h for h in liste_hotes(charger_yaml(INVENTAIRE)) if h.get("nom")}
|
|
|
|
|
|
def bootstrap() -> dict:
|
|
serveurs = {}
|
|
# Les universelles ne se recopient pas dans le plan (D-33) : les reinscrire ici
|
|
# reconstituerait, a chaque bootstrap, les lignes qu'on vient de retirer.
|
|
universelles = set(integrations_universelles())
|
|
for nom, h in hotes_inventaire().items():
|
|
fonction, _ = fonction_seq(nom)
|
|
srv = {"fonction": fonction, "etat": h.get("etat", "planifie")}
|
|
for cle_src, cle_dst in CHAMPS_PLAN:
|
|
v = str(h.get(cle_src, "")).strip()
|
|
if v:
|
|
srv[cle_dst] = int(v) if v.isdigit() else v
|
|
integrations = sorted(g for g in (h.get("groupes") or [])
|
|
if g.startswith("client_") and g not in universelles)
|
|
if integrations:
|
|
srv["integrations"] = integrations
|
|
serveurs[nom] = srv
|
|
return {"serveurs": serveurs}
|
|
|
|
|
|
def lister() -> None:
|
|
registre = charger_serveurs(FICHIER)
|
|
serveurs = registre.get("serveurs", {})
|
|
if not serveurs:
|
|
print("Aucun serveur. Lance: make serveurs-bootstrap")
|
|
return
|
|
nomenclature = charger_nomenclature(FICHIER_NOMENCLATURE)
|
|
hotes = hotes_inventaire()
|
|
for nom, srv in serveurs.items():
|
|
rec = reconcilier_serveur(nom, srv, hotes.get(nom), nomenclature)
|
|
d = rec["derive"]
|
|
if rec["absent_inventaire"]:
|
|
statut = "ABSENT de l'inventaire"
|
|
elif rec["divergences"]:
|
|
statut = "DIVERGENCE"
|
|
else:
|
|
statut = "reconcilie"
|
|
print(f"{nom} [fonction {srv.get('fonction')}, etat {srv.get('etat', '?')}]"
|
|
f" derive: vmid {d.get('vmid', '?')}, ip {d.get('adresse_ip', '?')}, vlan {d.get('vlan', '?')} -> {statut}")
|
|
for dv in rec["divergences"]:
|
|
print(f" ! {dv}")
|
|
|
|
|
|
def verifier() -> int:
|
|
registre = charger_serveurs(FICHIER)
|
|
nomenclature = charger_nomenclature(FICHIER_NOMENCLATURE)
|
|
valider_serveurs(registre, nomenclature)
|
|
hotes = hotes_inventaire()
|
|
divergents = []
|
|
for nom, srv in registre.get("serveurs", {}).items():
|
|
rec = reconcilier_serveur(nom, srv, hotes.get(nom), nomenclature)
|
|
if rec["divergences"] or rec["absent_inventaire"]:
|
|
divergents.append(nom)
|
|
if divergents:
|
|
print(f"Registre valide, mais {len(divergents)} serveur(s) non reconcilie(s): {', '.join(divergents)}")
|
|
else:
|
|
print("Registre des serveurs valide et reconcilie avec l'inventaire.")
|
|
return 0
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(description="Registre des serveurs (VM) du plan Set-OPS.")
|
|
sub = parser.add_subparsers(dest="commande", required=True)
|
|
sub.add_parser("lister", help="Affiche les serveurs et leur reconciliation avec l'inventaire.")
|
|
sub.add_parser("verifier", help="Valide le registre et signale les divergences.")
|
|
sub.add_parser("bootstrap", help="(Re)genere instance/plan/serveurs.yml depuis l'inventaire actuel.")
|
|
|
|
args = parser.parse_args()
|
|
try:
|
|
if args.commande == "lister":
|
|
lister()
|
|
elif args.commande == "verifier":
|
|
return verifier()
|
|
elif args.commande == "bootstrap":
|
|
registre = bootstrap()
|
|
valider_serveurs(registre, charger_nomenclature(FICHIER_NOMENCLATURE))
|
|
ecrire(registre)
|
|
print(f"instance/plan/serveurs.yml genere depuis l'inventaire ({len(registre['serveurs'])} serveurs).")
|
|
except Exception as exc:
|
|
print(f"erreur: {exc}", file=sys.stderr)
|
|
return 2
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|