Set-OPS-Public/docs/plan-et-generation.md
Daniel Allaire ac85278366 preuve : P34 — chaque document declare son lecteur (D-74)
La refonte de ce matin posait une convention. Une convention qu'on n'outille
pas tient tant que quelqu'un y pense : c'est le raisonnement de D-70, applique
au corpus documentaire.

Etat de depart mesure : 2 documents sur 34 declaraient leur lecteur. Les 32
autres disaient leur SUJET — ce qui avait enfoui le runbook de reprise le plus
utile du depot au §6 de autorisation.md.

Les 38 le declarent desormais, lecteur determine document par document et non
colle au gabarit : l'exploitant (devis, migration de tenant, cycle de vie,
gabarit d'or), le mainteneur (conceptions, registres, carte), le lecteur
externe (ecosysteme-chezlepro), l'agent IA (MISE-A-JOUR-CODEX-CLAUDE).

Deux exemptions DERIVEES, pas listees — un chemin en dur aurait vieilli a la
premiere page ajoutee : un document qui s'annonce genere, et un fragment sans
titre. Les 13 exemptes verifies un par un ; aucun document ecrit a la main
n'est exempte par accident. La preuve ne lit que l'EN-TETE, ce qui empeche
frontiere-opnsense.md et plan-et-generation.md — qui parlent de generation
dans leur corps — d'etre exemptes a tort.

Eprouvee dans les deux sens. Elle a echoue seule des sa premiere execution en
nommant deux documents que mon inventaire avait manques (docs/audit/). Puis
test negatif delibere : declaration retiree de meta-classe.md -> ECHEC la
nommant ; restauree -> OK.

Ce qu'elle ne teste pas : que le lecteur declare soit le BON. Ca se juge en
revue ; elle garantit qu'on a du y penser.

P01–P34. Comptes perimes corriges au passage (AGENTS.md et devis-services.md
annoncaient encore 30 preuves).

Verifie : prouver.py 0 (34 OK), plan-recette inchange.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 07:53:04 -04:00

8.6 KiB
Raw Blame History

Le plan et la génération d'inventaire (méta-classe)

Pour qui : le mainteneur — le plan et la génération de l'inventaire, à fond.

Set-OPS ne s'édite plus comme un inventaire à la main : on décrit un plan, et l'inventaire Ansible en est généré. Le dépôt est la définition ; chaque VM en est une instance. Ce document décrit le modèle, les registres, les commandes et le flux de travail.

Règle d'or : instance/inventories/production/hosts.yml est GÉNÉRÉ. Ne jamais l'éditer à la main. On édite le plan puis on régénère (make instancier-appliquer).


1. Le modèle : deux ancres, cinq liens

L'écosystème = des serveurs (VM) et des applications, reliés à leurs bases, leurs domaines et leurs dépendances :

serveur (VM)  ──fournit──▶ capacités (= groupes/rôles Ansible)
application   ──tourne_sur──▶ serveur          (une VM peut porter N applications)
application   ──requiert──▶ application(s)       (DNS, PKI, BD…)
application   ──utilise──▶ base(s)               (via le DSN)
application   ──expose──▶ domaine(s) public(s)    (le FQDN, derrière l'edge)
base          ──hébergée_sur──▶ serveur de BD

L'application est l'entité pivot : tout ce qui décrit « ce qui tourne et à quoi c'est connecté » pend d'elle. Le groupe Ansible n'est plus une cible de liaison — seulement une capacité qu'une VM fournit (le rôle appliqué).


2. Les registres (source unique de vérité)

Tous sous docs/, machine-lisibles, validés, consommés par le GUI, le CLI et Ansible.

Registre Décrit Champs clés
nomenclature.yml nommage & adressage fonctions (catégorie/service), categories (VLAN/sous-réseau/passerelle), supernet
serveurs.yml les VM du plan fonction, etat (actif/planifie), placement Proxmox (noeud/stockage/disque/memoire/coeurs), integrations (les client_* facultatives seulement — les universelles viennent du rôle, voir integrations-vm.md)
applications.yml les applications groupe (capacité/rôle), hote (VM), port, requiert, expose, (+ bases via consommateur)
bases-donnees.yml serveurs de BD + bases serveurs_bd ; bases_donnees : serveur/base/proprietaire/secret(Vault), consommateur + portee (application/groupe/hote), usage
domaines.yml zones DNS publiques domaines_publics : autorite, edge, secondaires, dnssec, mail
dependances-groupes.yml prérequis entre groupes requiert_groupes_actifs

Dérivations clés

  • Nommage/adressage : tout part de la fonction de l'hôte (web-frontal-03). VMID = 9·catégorie·service·NN, VLAN = catégorie.vlan, IP = 10.0.<vlan>.(service×10 + NN). Voir docs/nomenclature-vm.md.
  • DSN (lien application↔base) : <type>://<proprietaire>:<secret>@<hôte>:<port>/<base>. Une application reçoit les bases où (portee=application ET consommateur=elle) OU (portee=groupe ET consommateur=son groupe) OU (portee=hote ET consommateur=son hôte).
  • Exposition DNS : application.expose: [fqdn] + l'edge du domaine parent → serveur_nginx génère le vhost (application → hôte → IP:port).

3. La génération (méta-classe)

make instancier produit hosts.yml depuis le plan :

  • host vars : ansible_host/ansible_user + proxmox_* (IP/VMID/VLAN/passerelle dérivés de la nomenclature ; placement/taille depuis serveurs.yml).
  • groupes d'une VM = socle (serveur_debian + serveur_durci)
    • services (les groupe des applications de l'hôte)
    • intégrations universelles (politique du rôle : roles/client_*/meta/integration.yml)
    • intégrations facultatives (serveurs.yml: integrations)
    • état (hotes_actifs / hotes_planifies).

La comparaison est sémantique (via ansible-inventory --list, formatage ignoré). « Diff vide » = le plan reproduit exactement l'inventaire courant ; c'est le feu vert pour appliquer.


4. Le flux de travail

éditer le PLAN  ──▶  make instancier (revoir le diff)  ──▶  make instancier-appliquer  ──▶  déployer
 (GUI ou CLI)         (que va-t-il changer ?)                (régénère hosts.yml)            (make deployer)

Via le GUI — make inventaire-ui

Cinq vues :

Vue Rôle
Inventaire lecture seule (inventaire généré) — vue d'ensemble des hôtes
Serveurs éditer les VM du plan : fonction/état/placement/intégrations (VMID·IP·VLAN dérivés en direct) ; bouton « Appliquer le plan »
Chaîne vue holistique par hôte : groupes → rôles, et par application ses expose / requiert / bases (DSN)
Applications éditer les applications : groupe/hôte/port/requiert/expose
Bases éditer serveurs de BD et bases (portée + consommateur), DSN affiché

Édition → Sauvegarder (écrit le registre) → Appliquer le plan (régénère hosts.yml). L'écriture directe de l'inventaire est refusée (409).

Via le CLI / make

Chaque registre a son script miroir et ses cibles make :

Domaine Lister Vérifier Bootstrap (depuis l'inventaire)
Serveurs make serveurs make serveurs-verifier make serveurs-bootstrap
Applications make applications make applications-verifier make applications-bootstrap
Bases make bases make bases-verifier
Domaines make domaines make domaines-verifier

Génération :

make instancier             # génère hosts.genere.yml (gitignoré) + diff sémantique
make instancier-appliquer   # régénère hosts.yml (refuse si diff non vide ; FORCE=1 pour forcer)

Validation globale : make inventaire-verifier (ansible-inventory + tous les registres + node --check du JS du GUI).


5. Garde-fous

  • Validateurs : chaque registre est validé (références connues, énumérations, unicité). Un expose sans domaine parent, un requiert fantôme, une portee inconnue, une fonction absente → rejet.
  • Diff vide : instancier-appliquer refuse d'écraser l'inventaire si le plan ne le reproduit pas (sauf FORCE=1 pour un changement intentionnel).
  • node --check : le JS embarqué du GUI est vérifié (scripts/verifier_gui.py, intégré à make inventaire-verifier) — une erreur de syntaxe JS casse toute la page.
  • git : hosts.yml est versionné ; git diff / git checkout est le filet.
  • Secrets : jamais en clair ; secret: nomme une variable Ansible Vault.

6. Réutilisation de la règle (Ansible)

La règle de résolution vit une seule fois, en Python (scripts/inventory_rules.py), et est exposée à Ansible par un filter plugin (filter_plugins/registres.py) : bases_de_application, applications_de_hote, expositions_des_applications, chaine_connexion. Les playbooks par application (serveur_web_dorsal/_frontaux) itèrent ainsi sur les applications de l'hôte et résolvent leurs DSN.


7. Amorçage / reconstruction

Pour (re)construire le plan depuis un inventaire existant :

make serveurs-bootstrap        # VM -> instance/plan/serveurs.yml (fonction/état/placement/integrations)
make applications-bootstrap    # services serveurs_* (hors socle) -> instance/plan/applications.yml
make instancier                # vérifier le diff vide

C'est ainsi que le plan a été initialisé sans perte, avec diff vide vérifié.


8. Moteur et instance : deux dépôts

Le moteur (ce dépôt, Set-OPS) est générique et partageable ; il ne contient aucune donnée d'instance. Une instance (le plan + l'inventaire d'un loup) vit dans son propre dépôt (ex. OPS-monatelier).

Le moteur localise l'instance via SETOPS_INSTANCE (défaut : instance). Deux modèles :

  • Modèle A — dépôts frères (en cours) : moteur et instance côte à côte ; un symlink instance -> ../OPS-monatelier (gitignoré) fait que le défaut résout l'instance sans configuration. Idéal quand on développe le moteur et l'instance.
  • Modèle B — moteur en sous-module (futur, pour la meute) : l'instance épingle une version du moteur ; SETOPS_INSTANCE pointe la racine de l'instance.

Pour brancher une instance (modèle A) :

cd Set-OPS
ln -s ../OPS-monatelier instance     # ou : export SETOPS_INSTANCE=/chemin/instance
make inventaire-verifier            # lit l'instance via le symlink

Le moteur écrit hosts.yml (généré) dans le dépôt d'instance, jamais dans le sien.