Some checks are pending
verifier / verifier (push) Waiting to run
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
205 lines
11 KiB
Markdown
205 lines
11 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/<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 :
|
||
|
||
```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`, `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 :
|
||
|
||
```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'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) :
|
||
|
||
```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.
|