CHAMPS_ECRITS_A_LA_MAIN est vide. Serveurs et applications, les deux plus gros, sont passes au generateur — chargement, rendu et sauvegarde. L EPREUVE QUI COMPTE. Ouvrir chaque vue et enregistrer sans rien toucher doit renvoyer exactement le plan qu on vient de lire : 14 serveurs, 25 applications, 2 domaines, 4 bases, IDENTIQUE partout. C est ce qui separe un formulaire genere d un formulaire qui en a l air — un champ visible a l ecran et perdu en silence a l enregistrement serait le pire des deux mondes. test_rendu_gui.py le mesure a chaque make prouver. TROIS DEFAUTS TROUVES EN CHEMIN. Le formulaire annoncait des defauts INVENTES : 2048 Mo, 2 coeurs, 16G. Il n existe aucun defaut fixe — deriver_ressources calcule depuis les roles portes (1024 et 1 pour infra-pki-01, 5632 et 4 pour collab-01). Un repere faux fait croire qu on connait la valeur. Le schema nomme le champ derive, et l ecran montre la valeur reelle de cet hote. L option vide d un select dit desormais ce qu elle produira : « (defaut : asgard) ». Une SECONDE occurrence du defaut d hier dormait dans sourceDeValeurs : elle lisait encore data.nomenclature. Elle n avait jamais leve parce que la vue Serveurs, seule a emprunter cette source, avait un formulaire ecrit a la main. Elle a leve a la seconde ou le generateur l a prise. Le banc ne voit que les chemins vivants : verifier_gui.py fait donc aussi une verification STATIQUE, qui voit ce qui dort. La validation client s accrochait a data-v, pose a la main sur trois champs. Le formulaire genere l aurait perdu et la validation serait passee au vert sur ZERO champ. Le generateur marque chaque controle, et la sauvegarde refuse si elle n en inspecte aucun. DEUX CHAMPS GARDENT LEUR EDITEUR, et le schema le dit (x-editeur) : la matrice des integrations montre les universelles et les exemptions, et l editeur de liens contraint le role a meta/liens.yml. Le generateur s efface plutot que de remplacer un editeur qui en sait plus que lui. LIMITE : je n ai toujours pas ouvert ces pages dans un navigateur. make prouver : CONFORME, 61 OK, 0 echec, 1 saute. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
11 KiB
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.ymlest 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'ordreprincipal, puisproduction(scripts/inventory_rules.py:ORDRE_INVENTAIRE; pour la construction du gabarit,ORDRE_INVENTAIRE_MODELEessaielabd'abord). La flotte utiliseprincipal; le modèle public livré dansexemples/modeles/socle/utiliseproduction, 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
indexet de lafonctionde 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. Voirdocs/nomenclature-vm.md. (Les formules à cinq chiffres et le réseau unique10.0.xqui 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'edgedu domaine parent →serveur_nginxgé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 depuisserveurs.yml). - groupes d'une VM = socle (
serveur_debian+serveur_durci)- services (les
groupedes 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).
- services (les
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).
Les formulaires des six registres sont générés depuis docs/audit/schema-plan.json
(make schema), dérivé des constantes du moteur. La sauvegarde en dérive aussi : un
champ ajouté au plan apparaît à l'écran et arrive au fichier. Deux exceptions
déclarées au schéma (x-editeur) : la matrice des intégrations et l'éditeur de liens
gardent leur éditeur propre, plus riche que ce que le schéma sait dire.
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
exposesans domaine parent, unrequiertfantôme, uneporteeinconnue, unefonctionabsente → rejet. - Diff vide :
instancier-appliquerrefuse d'écraser l'inventaire si le plan ne le reproduit pas (saufFORCE=1pour 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.ymlest versionné ;git diff/git checkoutest 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_INSTANCEpointe 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.