Set-OPS-Public/docs/plan-et-generation.md
Daniel Allaire 88033f37b8
Some checks are pending
verifier / verifier (push) Waiting to run
GUI : la vue Nomenclature, et deux fautes que mes bancs ne voyaient pas
LA VUE. La nomenclature etait le seul registre que le GUI ne savait pas
ecrire du tout : ajouter une fonction exigeait d ouvrir le YAML. Elle a
sa vue, et son formulaire est GENERE depuis le schema. Deuxieme registre
sur six. couverture_gui verifier passe : les 28 champs des plans reels
sont editables.

Elle n est pas un registre comme les autres : elle decrit la REGLE dont
VMID, VLAN, adresse et passerelle se derivent. Chaque fonction montre ce
qu elle derive et les VM qui la portent ; l index est montre mais pas
editable, parce qu il est alloue par le site ; valider_nomenclature
refuse de retirer une fonction encore portee, ou de designer une zone
non declaree.

DEUX FAUTES, ET POURQUOI MES BANCS NE LES VOYAIENT PAS.

Le formulaire des bases, livre la veille, etait casse dans un navigateur.
Il lisait data.schema, or il n existe aucun data global : c est une const
locale de charger(). ReferenceError a l ouverture, et zone morte dans
sauvegarderBases. Je l avais eprouve sous node EN LUI PASSANT data : le
banc reproduisait la fonction, pas sa portee. D ou test_rendu_gui.py, qui
charge le JS entier dans un DOM simule et dessine les douze vues, avec son
controle negatif.

Le schema decrivait reservations comme une table de zones ; le fichier
reel est un bloc plat. P61 comparait des NOMS aplatis, donc ne voyait
rien. Elle compare desormais aussi la FORME.

ECRIRE SANS DEPLACER UN COMMENTAIRE. _fusion_chirurgicale remplace le
bloc entier des qu une valeur change : quinze entrees compactes devenaient
42 lignes, et le commentaire du poste d exploitation se retrouvait en tete
du bloc, ou il affirmait que collab etait le poste d exploitation. Un
commentaire deplace n est pas laid, il est faux. _fusion_table edite les
tables ligne a ligne ; le diff fait trois lignes.

Au passage : sort_keys triait le schema, donc l ordre des cases a l ecran
(reserve_max avant reserve_min) ; et _ecrire_index_nomenclature ecrivait
encore par write_text, oubliee au passage des ecritures atomiques.

LIMITE : deux registres sur six sont generes, et je n ai toujours pas
ouvert cette page dans un navigateur.

make prouver : CONFORME, 60 OK, 0 echec, 1 saute.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-08 17:08:45 -04:00

11 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/<inventaire>/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).

<inventaire> n'est pas un nom, c'est une place. Le dépôt n'impose pas comment une instance nomme son inventaire : le moteur le cherche, dans l'ordre principal, puis production (scripts/inventory_rules.py : ORDRE_INVENTAIRE ; pour la construction du gabarit, ORDRE_INVENTAIRE_MODELE essaie lab d'abord). La flotte utilise principal ; le modèle public livré dans exemples/modeles/socle/ utilise production, et le QUICKSTART l'écrit tel quel parce que c'est ce que son lecteur a sous la main. Les documents de doctrine, eux, écrivent <inventaire> : coder l'un des deux noms en dur y serait faux pour la moitié des lecteurs — et ça l'a été jusqu'au 2026-09-06.


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é)

Machine-lisibles, validés, consommés par le GUI, le CLI et Ansible. Ils vivent dans l'instance (instance/plan/), pas dans le moteur — c'est toute la séparation moteur/instance. Seul dependances-groupes.yml est sous docs/, parce qu'il décrit une propriété des rôles, la même pour toutes les instances.

Registre Décrit Champs clés
nomenclature.yml nommage & adressage index (le seed, seul champ d'adressage), fonctions (catégorie/service), categories (libellés de zones), cidr_hote, reservations. Ni supernet, ni vlan, ni passerelle : P20 les refuse — ils se dérivent.
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 du seed index et de la fonction de l'hôte. supernet = 10.<index>.0.0/16, zone = 10.<index>.(15+catégorie), VLAN = 1000 + index×10 + zone, VMID = <VLAN><octet-hôte><rang> — neuf chiffres, miroir de l'IP. Voir docs/nomenclature-vm.md. (Les formules à cinq chiffres et le réseau unique 10.0.x qui figuraient ici décrivaient le modèle d'avant le multi-instance.)
  • 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

Douze vues, éditables ou dérivées :

Vue Rôle
Serveurs (éditable) les VM du plan : fonction/état/placement/intégrations (VMID·IP·VLAN dérivés en direct) ; bouton « Appliquer le plan »
Applications (éditable) groupe/hôte/port/requiert/expose, et les liens acceptés par le rôle
Bases (éditable) serveurs de BD et bases (portée + consommateur), DSN affiché (secret masqué)
Domaines (éditable) zones publiques vs internes, autorité · edge · DNSSEC, expositions
Nomenclature (éditable) le modèle dont tout l'adressage dérive : zones, fonctions (catégorie · service), réservations. Chaque fonction affiche ce qu'elle dérive (VLAN, sous-réseau, bloc d'hôtes) et les VM qui la portent. L'index y est montré, pas éditable : il est alloué par le site
Intégrations (éditable) la matrice serveurs × intégrations ; les universelles en ✓ non décochables
Intrants (éditable) les intrants de base — identité, Proxmox, fabric, et la liste de rappel des secrets (lecture seule)
Inventaire (lecture seule) l'inventaire généré — vue d'ensemble des hôtes
Chaîne (lecture seule) vue holistique par hôte : groupes → rôles, et par application ses expose / requiert / bases
Flux (lecture seule) la matrice d'audit des flux — source de nftables et justification lisible
Couches (lecture seule) l'ordre de déploiement en six couches
Réseau (lecture seule + bascule) la flotte multi-instances, les collisions d'index, et le bouton « Activer »

É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, serveur_web_frontal) 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'une organisation) 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, quand plusieurs écosystèmes vivront chez des exploitants distincts) : 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.