# Le plan et la génération d'inventaire (méta-classe) 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 `clients_*`) | | `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..(service×10 + NN)`. Voir `docs/nomenclature-vm.md`. - **DSN** (lien application↔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** (`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.