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>
182 lines
8.6 KiB
Markdown
182 lines
8.6 KiB
Markdown
# 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 :
|
||
|
||
```bash
|
||
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 :
|
||
|
||
```bash
|
||
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) :
|
||
|
||
```bash
|
||
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.
|