Decouplage moteur/instance Phase 2b : sortir l'inventaire

L'inventaire (hotes generes, group_vars, vault) quitte inventories/ pour
instance/inventories/. Suivent : ansible.cfg, Makefile (export
SETOPS_INSTANCE ?= instance + chemins derives), scripts (INSTANCE/),
config_proxmox.py, .gitignore. Docs primaires (AGENTS/README/
plan-et-generation/architecture) refletent le layout instance/.

Non destructif : diff vide, ansible-lint 0 echec, make inventaire-verifier
OK. (Docs secondaires a rafraichir ; Phase 3 : deux depots.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Daniel Allaire 2026-06-24 09:16:18 -04:00
parent 17f65efb18
commit 3b7f3098f2
36 changed files with 42 additions and 39 deletions

2
.gitignore vendored
View file

@ -1,6 +1,6 @@
*.retry
# Inventaire genere depuis le plan (artefact, make instancier ; pas une source).
inventories/*/hosts.genere.yml
instance/inventories/*/hosts.genere.yml
.vault-pass
facts_cache/
.ansible/

View file

@ -27,7 +27,7 @@ Piliers de lécosystème :
Propriétés visées, avec leurs nuances honnêtes :
- **Souverain / auto-suffisant** : cest lobjectif. Aucune dépendance à un service externe pour la confiance, lidentité, le nom ou la communication. Cela justifie le choix de construire plutôt quassembler des SaaS.
- **Déclaratif et convergent** : létat voulu est décrit dans des registres machine-lisibles (`docs/nomenclature.yml`, `docs/dependances-groupes.yml`, `docs/bases-donnees.yml`) et appliqué par les groupes Ansible. Ces registres sont la **source unique de vérité**.
- **Déclaratif et convergent** : létat voulu est décrit dans des registres machine-lisibles (`instance/plan/nomenclature.yml`, `docs/dependances-groupes.yml`, `instance/plan/bases-donnees.yml`) et appliqué par les groupes Ansible. Ces registres sont la **source unique de vérité**.
- **Pas (encore) auto-réparé** : la convergence est pilotée par lopérateur (GUI / CLI / `make`), pas une boucle fermée dauto-remédiation.
- **Définition avant déploiement** : une grande partie est planifiée et validée (`--syntax-check`, `ansible-lint`) mais pas encore exécutée contre des VM réelles. Ne jamais présenter un rôle non déployé comme « en production ».
@ -40,10 +40,10 @@ Conséquence pour le travail : préserver la discipline qui tient lensemble
`Set-OPS` se pilote par un **plan**, pas par lédition directe de linventaire.
Linventaire Ansible est **généré** depuis le plan.
**RÈGLE DOR : `inventories/production/hosts.yml` est un artefact GÉNÉRÉ. Ne jamais léditer à la main.** On édite le *plan*, puis on régénère.
**RÈGLE DOR : `instance/inventories/production/hosts.yml` est un artefact GÉNÉRÉ. Ne jamais léditer à la main.** On édite le *plan*, puis on régénère.
- L**application** est lentité pivot ; le **groupe** Ansible nest quune capacité (le rôle appliqué), plus une cible de liaison.
- Le plan vit dans des registres machine-lisibles : `docs/serveurs.yml` (les VM), `docs/applications.yml` (les services et leurs liens `requiert`/`utilise`/`expose`), `docs/bases-donnees.yml`, `docs/domaines.yml`, `docs/nomenclature.yml`.
- Le plan vit dans des registres machine-lisibles : `instance/plan/serveurs.yml` (les VM), `instance/plan/applications.yml` (les services et leurs liens `requiert`/`utilise`/`expose`), `instance/plan/bases-donnees.yml`, `instance/plan/domaines.yml`, `instance/plan/nomenclature.yml`.
- Génération : `make instancier` (génère + diff sémantique), `make instancier-appliquer` (régénère `hosts.yml`, refuse si le diff nest pas vide — `FORCE=1` pour un changement intentionnel).
- Flux : **éditer le plan → `make instancier` (revoir le diff) → `make instancier-appliquer` → déployer**. Via le GUI : vues **Serveurs** et **Applications**, puis « Appliquer le plan » (la vue Inventaire est en lecture seule).
- VMID / IP / VLAN / passerelle sont **dérivés** de la `fonction` via la nomenclature ; les groupes dune VM sont dérivés (socle + services des applications + intégrations + état).
@ -159,7 +159,7 @@ Avant de proposer un changement comme terminé, vérifier au minimum la syntaxe
Exemple pour le template Debian 13 Proxmox :
```bash
ansible-playbook -i inventories/lab/hosts.yml playbooks/modeles_vm/debian13_proxmox_preparer.yml --syntax-check
ansible-playbook -i instance/inventories/lab/hosts.yml playbooks/modeles_vm/debian13_proxmox_preparer.yml --syntax-check
```
Pour un autre playbook, remplacer le chemin par le playbook concerné.
@ -595,7 +595,7 @@ Le nettoyage final avant conversion en template doit être protégé par une con
Exemple :
```bash
ansible-playbook -i inventories/lab/hosts.yml playbooks/modeles_vm/debian13_proxmox_nettoyer.yml -e template_cleanup_confirm=true
ansible-playbook -i instance/inventories/lab/hosts.yml playbooks/modeles_vm/debian13_proxmox_nettoyer.yml -e template_cleanup_confirm=true
```
Le nettoyage peut inclure :

View file

@ -3,6 +3,7 @@
## 2026-06-23
### Modifié
- **Découpage moteur/instance — Phase 2b : sortir l'inventaire dans `instance/inventories/`.** L'inventaire (hôtes générés, `group_vars`, vault) quitte `inventories/` pour `instance/inventories/`. Suivent : `ansible.cfg`, le `Makefile` (`export SETOPS_INSTANCE ?= instance`, chemins dérivés), les scripts (`INSTANCE / "inventories/..."`), `config_proxmox.py`, `.gitignore`. Les docs primaires (`AGENTS.md`, `README.md`, `docs/plan-et-generation.md`, `docs/architecture-set-ops.md`) reflètent le layout `instance/`. Non destructif : diff vide, `ansible-lint` 0 échec, `make inventaire-verifier` OK. *(Docs secondaires à rafraîchir ; Phase 3 : deux dépôts.)*
- **Découpage moteur/instance — Phase 2a : sortir le plan dans `instance/plan/`.** Les 5 registres (`serveurs`, `applications`, `bases-donnees`, `domaines`, `nomenclature`) quittent `docs/` pour `instance/plan/`. Les scripts les localisent via **`SETOPS_INSTANCE`** (variable d'env, défaut `<dépôt>/instance`) ; les rôles/playbooks via **`setops_plan_dir`** (group_vars d'instance). `docs/` ne garde que les guides `.md` + `dependances-groupes.yml` (moteur). `.ansible-lint` exclut `instance/plan/` (registres de données). Non destructif : diff vide, `ansible-lint` 0 échec, `include_vars` via `setops_plan_dir` testé. *(Phase 2b : sortir l'inventaire ; Phase 3 : deux dépôts.)*
- **Découpage moteur/instance — Phase 1b : neutraliser la marque `chezlepro`.** Les noms de fichiers gérés par les rôles (`99-chezlepro.conf`, `chezlepro_filter`, `chezlepro-bind.conf`, `/etc/redis/chezlepro.conf`, etc.) et les 14 templates correspondants passent à la marque neutre du moteur **`setops`** (`git mv` + références `src`/`dest`). Un loup ne déploie plus de fichiers marqués « chezlepro ». Cohérence `src ↔ template` vérifiée, `ansible-lint` 0 échec, diff vide. (Les README gardent leurs exemples ; génériciser plus tard.)
- **Découpage moteur/instance — Phase 1 : délier les rôles du domaine Chezlepro.** Introduction d'une variable d'identité d'instance **`domaine_interne`** (dans `inventories/*/group_vars/all.yml`). Les 13 rôles qui codaient `chezlepro.internal` en dur (zones DNS, FQDN, relais SMTP, AC, etc.) utilisent désormais `{{ domaine_interne }}` ; le `base_dn` LDAP et le domaine court sont **dérivés** (`dc={{ domaine_interne.split('.') | join(',dc=') }}` → `dc=chezlepro,dc=internal`, ou `dc=acme,dc=local` pour un autre loup). Comportement **identique** pour Chezlepro (qui pose `domaine_interne: chezlepro.internal`), mais le moteur devient générique — première brique pour partager l'outil à la meute (`docs/positionnement.md`). Ajout d'`exemples/instance.exemple.yml`. Non destructif : diff de génération vide, `ansible-lint` 0 échec. *(La marque `chezlepro` sur les noms de fichiers gérés — `99-chezlepro.conf`, `chezlepro_filter` — sera neutralisée en Phase 1b ; sortie du plan en Phase 2 ; deux dépôts en Phase 3.)*

View file

@ -5,8 +5,9 @@ export ANSIBLE_LOCAL_TEMP ?= $(CURDIR)/.ansible/tmp
export ANSIBLE_SSH_CONTROL_PATH_DIR ?= $(CURDIR)/.ansible/cp
export ANSIBLE_SSH_ARGS ?= -F /dev/null -o ControlMaster=no
INVENTAIRE_LAB ?= inventories/lab/hosts.yml
INVENTAIRE_PRODUCTION ?= inventories/production/hosts.yml
export SETOPS_INSTANCE ?= instance
INVENTAIRE_LAB ?= $(SETOPS_INSTANCE)/inventories/lab/hosts.yml
INVENTAIRE_PRODUCTION ?= $(SETOPS_INSTANCE)/inventories/production/hosts.yml
FICHIER_INVENTAIRE ?= $(INVENTAIRE_PRODUCTION)
FICHIER_DEPENDANCES ?= docs/dependances-groupes.yml
GROUPE_MODELE ?= modeles_vm
@ -121,7 +122,7 @@ aide:
@printf '%s\n' ' Graphe de production:'
@printf '%s\n' ' make inventaire'
@printf '%s\n' ' Graphe explicite:'
@printf '%s\n' ' make inventaire-graphe FICHIER_INVENTAIRE=inventories/production/hosts.yml'
@printf '%s\n' ' make inventaire-graphe FICHIER_INVENTAIRE=$(SETOPS_INSTANCE)/inventories/production/hosts.yml'
@printf '%s\n' ' Verifier les inventaires:'
@printf '%s\n' ' make inventaire-verifier'
@printf '%s\n' ' Lister les donnees brutes:'
@ -144,7 +145,7 @@ aide:
@printf '%s\n' 'Variables frequentes'
@printf '%s\n' ' HOTE=web-frontal-01 GROUPE=serveur_debian GROUPES="serveur_debian serveur_durci"'
@printf '%s\n' ' VMID=95301 VLAN=15 ADRESSE_IP=10.1.15.31 PASSERELLE=10.1.15.1'
@printf '%s\n' ' FICHIER_INVENTAIRE=inventories/production/hosts.yml FICHIER_DEPENDANCES=docs/dependances-groupes.yml CONFIRMER=true'
@printf '%s\n' ' FICHIER_INVENTAIRE=$(SETOPS_INSTANCE)/inventories/production/hosts.yml FICHIER_DEPENDANCES=docs/dependances-groupes.yml CONFIRMER=true'
.PHONY: lint
lint: ansible-runtime
@ -244,7 +245,7 @@ deployer:
printf '%s\n' 'Refus: aucun playbook applicable pour HOTE=$(HOTE).'; \
exit 2; \
fi; \
vault_chiffre="$$(grep -rlsIF '$$ANSIBLE_VAULT' inventories/production/group_vars 2>/dev/null | head -1 || true)"; \
vault_chiffre="$$(grep -rlsIF '$$ANSIBLE_VAULT' $(SETOPS_INSTANCE)/inventories/production/group_vars 2>/dev/null | head -1 || true)"; \
if [[ -n "$$vault_chiffre" && -z "$${ANSIBLE_VAULT_PASSWORD_FILE:-}" ]]; then \
if [[ -t 0 ]]; then \
read -r -s -p 'Mot de passe du vault Ansible: ' mdp; echo; \
@ -284,7 +285,7 @@ verifier-deploiement: ansible-runtime
printf '%s\n' 'Refus: aucun playbook applicable pour HOTE=$(HOTE).'; \
exit 2; \
fi; \
vault_chiffre="$$(grep -rlsIF '$$ANSIBLE_VAULT' inventories/production/group_vars 2>/dev/null | head -1 || true)"; \
vault_chiffre="$$(grep -rlsIF '$$ANSIBLE_VAULT' $(SETOPS_INSTANCE)/inventories/production/group_vars 2>/dev/null | head -1 || true)"; \
if [[ -n "$$vault_chiffre" && -z "$${ANSIBLE_VAULT_PASSWORD_FILE:-}" ]]; then \
if [[ -t 0 ]]; then \
read -r -s -p 'Mot de passe du vault Ansible: ' mdp; echo; \
@ -344,7 +345,7 @@ cloner-vm: ansible-runtime
extra_vars+=( -e proxmox_clone_vlan="$(VLAN)" ); \
[[ -n "$(DEMARRER)" ]] && extra_vars+=( -e proxmox_clone_demarrer="$(DEMARRER)" ); \
vault_args=(); \
vault_file="inventories/lab/group_vars/proxmox.vault.yml"; \
vault_file="$(SETOPS_INSTANCE)/inventories/lab/group_vars/proxmox.vault.yml"; \
if [[ -f "$$vault_file" ]]; then \
read -r premiere_ligne < "$$vault_file" || true; \
case "$$premiere_ligne" in \

View file

@ -4,7 +4,7 @@ Dépôt Ansible central pour construire, configurer, maintenir et documenter les
## Mission
`Set-OPS` ne se limite plus à l'exploitation : il **définit et construit l'écosystème numérique souverain de Chezlepro Inc.** — une infrastructure interne auto-suffisante, sans dépendance SaaS, décrite en code et pilotée par des registres machine-lisibles qui font office de **source unique de vérité** (`docs/serveurs.yml`, `docs/applications.yml`, `docs/bases-donnees.yml`, `docs/domaines.yml`, `docs/nomenclature.yml`, `docs/dependances-groupes.yml`).
`Set-OPS` ne se limite plus à l'exploitation : il **définit et construit l'écosystème numérique souverain de Chezlepro Inc.** — une infrastructure interne auto-suffisante, sans dépendance SaaS, décrite en code et pilotée par des registres machine-lisibles qui font office de **source unique de vérité** (`instance/plan/serveurs.yml`, `instance/plan/applications.yml`, `instance/plan/bases-donnees.yml`, `instance/plan/domaines.yml`, `instance/plan/nomenclature.yml`, `docs/dependances-groupes.yml`).
Piliers de l'écosystème :
@ -23,13 +23,13 @@ Cadre et règles d'autorité : voir `AGENTS.md` (section « Mission et identité
## Le plan : on édite, l'inventaire se génère
`Set-OPS` se pilote par un **plan**, pas par l'édition directe de l'inventaire.
`inventories/production/hosts.yml` est **généré** depuis le plan — **ne pas l'éditer à la main**.
`instance/inventories/production/hosts.yml` est **généré** depuis le plan — **ne pas l'éditer à la main**.
```
éditer le PLAN → make instancier (revoir le diff) → make instancier-appliquer → make deployer
```
- **Plan** : `docs/serveurs.yml` (les VM), `docs/applications.yml` (les services et leurs liens), `docs/bases-donnees.yml`, `docs/domaines.yml`, dérivés via `docs/nomenclature.yml`.
- **Plan** : `instance/plan/serveurs.yml` (les VM), `instance/plan/applications.yml` (les services et leurs liens), `instance/plan/bases-donnees.yml`, `instance/plan/domaines.yml`, dérivés via `instance/plan/nomenclature.yml`.
- **GUI** (`make inventaire-ui`) : vues **Serveurs** et **Applications** pour éditer, puis « Appliquer le plan » ; la vue **Inventaire** est en lecture seule.
- VMID / IP / VLAN sont **dérivés** de la `fonction` ; les groupes d'une VM sont dérivés des applications qui y tournent.
@ -98,13 +98,13 @@ make inventaire-ui
Planifier une VM passe désormais par le **plan**, pas par l'édition de l'inventaire :
1. déclarer la VM dans `docs/serveurs.yml` (vue **Serveurs** du GUI, ou `make serveurs`) — `fonction`, `etat`, placement ; VMID/IP/VLAN sont dérivés ;
2. déclarer les services qui y tournent dans `docs/applications.yml` (vue **Applications**) ;
1. déclarer la VM dans `instance/plan/serveurs.yml` (vue **Serveurs** du GUI, ou `make serveurs`) — `fonction`, `etat`, placement ; VMID/IP/VLAN sont dérivés ;
2. déclarer les services qui y tournent dans `instance/plan/applications.yml` (vue **Applications**) ;
3. régénérer l'inventaire :
```bash
make instancier # génère + diff sémantique (que va-t-il changer ?)
make instancier-appliquer # régénère inventories/production/hosts.yml
make instancier-appliquer # régénère instance/inventories/production/hosts.yml
```
Les anciennes commandes `make hote-planifier` / `hote-ajouter` / `hote-groupes` éditaient l'inventaire **directement** ; elles sont **supplantées** par le plan (l'inventaire est généré, ne pas l'éditer à la main).

View file

@ -1,5 +1,5 @@
[defaults]
inventory = inventories/lab/hosts.yml
inventory = instance/inventories/lab/hosts.yml
roles_path = roles
filter_plugins = filter_plugins
interpreter_python = auto_silent

View file

@ -1,7 +1,7 @@
# Architecture Set-OPS
Set-OPS définit et construit l'écosystème numérique souverain de Chezlepro. Il se
pilote par un **plan** : l'inventaire Ansible (`inventories/production/hosts.yml`)
pilote par un **plan** : l'inventaire Ansible (`instance/inventories/production/hosts.yml`)
est **généré** depuis le plan, pas édité à la main.
- Modèle, registres, commandes et flux de travail : **`docs/plan-et-generation.md`**.
@ -10,11 +10,11 @@ est **généré** depuis le plan, pas édité à la main.
## Entités du plan
```text
serveur (VM) docs/serveurs.yml fonction, état, placement, intégrations
application docs/applications.yml groupe (rôle), hôte, port, requiert, expose
base docs/bases-donnees.yml serveur de BD, base, propriétaire, secret (Vault), portée
domaine (DNS) docs/domaines.yml zone publique, autorité, edge
nomenclature docs/nomenclature.yml fonctions -> VMID / VLAN / IP (dérivés)
serveur (VM) instance/plan/serveurs.yml fonction, état, placement, intégrations
application instance/plan/applications.yml groupe (rôle), hôte, port, requiert, expose
base instance/plan/bases-donnees.yml serveur de BD, base, propriétaire, secret (Vault), portée
domaine (DNS) instance/plan/domaines.yml zone publique, autorité, edge
nomenclature instance/plan/nomenclature.yml fonctions -> VMID / VLAN / IP (dérivés)
```
L'**application** est l'entité pivot ; le **groupe** Ansible n'est qu'une capacité
@ -33,7 +33,7 @@ Les valeurs communes aux rôles doivent rester dans les defaults des rôles quan
Les groupes opérationnels de l'inventaire doivent correspondre à un playbook homonyme :
```text
inventories/production/hosts.yml
instance/inventories/production/hosts.yml
serveur_debian
playbooks/groupes/serveur_debian.yml

View file

@ -5,7 +5,7 @@ l'inventaire Ansible en est **généré**. Le dépôt est la définition ; chaqu
est une instance. Ce document décrit le modèle, les registres, les commandes et
le flux de travail.
> Règle d'or : **`inventories/production/hosts.yml` est GÉNÉRÉ. Ne jamais l'éditer
> 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`).
---
@ -144,8 +144,8 @@ itèrent ainsi sur les applications de l'hôte et résolvent leurs DSN.
Pour (re)construire le plan depuis un inventaire existant :
```bash
make serveurs-bootstrap # VM -> docs/serveurs.yml (fonction/état/placement/integrations)
make applications-bootstrap # services serveurs_* (hors socle) -> docs/applications.yml
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
```

View file

@ -26,7 +26,7 @@ INSTANCE = Path(os.environ.get("SETOPS_INSTANCE") or (RACINE / "instance"))
FICHIER = INSTANCE / "plan/applications.yml"
FICHIER_BASES = INSTANCE / "plan/bases-donnees.yml"
FICHIER_DOMAINES = INSTANCE / "plan/domaines.yml"
INVENTAIRE = RACINE / "inventories/production/hosts.yml"
INVENTAIRE = INSTANCE / "inventories/production/hosts.yml"
# Groupes 'serveurs_*' qui sont des capacites de SOCLE (sur toutes les VM),
# pas des applications a part entiere.

View file

@ -13,8 +13,9 @@ import yaml
RACINE = Path(__file__).resolve().parents[1]
FICHIER_PROXMOX = RACINE / "inventories/lab/group_vars/proxmox.yml"
FICHIER_VAULT = RACINE / "inventories/lab/group_vars/proxmox.vault.yml"
INSTANCE = Path(os.environ.get("SETOPS_INSTANCE") or (RACINE / "instance"))
FICHIER_PROXMOX = INSTANCE / "inventories/lab/group_vars/proxmox.yml"
FICHIER_VAULT = INSTANCE / "inventories/lab/group_vars/proxmox.vault.yml"
VALEURS_DEFAUT = {

View file

@ -1,7 +1,7 @@
#!/usr/bin/env python3
"""Generateur d'inventaire Set-OPS (Phase 3) : plan -> hosts.yml.
NON destructif : ecrit dans inventories/production/hosts.yml.genere et compare
NON destructif : ecrit dans instance/inventories/production/hosts.genere.yml et compare
SEMANTIQUEMENT (via ansible-inventory --list) avec l'inventaire actuel. Aucune
bascule tant que la comparaison n'est pas vide et validee.
@ -35,8 +35,8 @@ from inventory_rules import (
RACINE = Path(__file__).resolve().parents[1]
INSTANCE = Path(os.environ.get("SETOPS_INSTANCE") or (RACINE / "instance"))
INVENTAIRE = RACINE / "inventories/production/hosts.yml"
GENERE = RACINE / "inventories/production/hosts.genere.yml"
INVENTAIRE = INSTANCE / "inventories/production/hosts.yml"
GENERE = INSTANCE / "inventories/production/hosts.genere.yml"
FICHIER_SERVEURS = INSTANCE / "plan/serveurs.yml"
FICHIER_APPLICATIONS = INSTANCE / "plan/applications.yml"
FICHIER_NOMENCLATURE = INSTANCE / "plan/nomenclature.yml"

View file

@ -38,8 +38,8 @@ from inventory_rules import (
RACINE = Path(__file__).resolve().parents[1]
INSTANCE = Path(os.environ.get("SETOPS_INSTANCE") or (RACINE / "instance"))
INVENTAIRE_DEFAUT = RACINE / "inventories/production/hosts.yml"
INVENTAIRE_PRODUCTION = (RACINE / "inventories/production/hosts.yml").resolve()
INVENTAIRE_DEFAUT = INSTANCE / "inventories/production/hosts.yml"
INVENTAIRE_PRODUCTION = (INSTANCE / "inventories/production/hosts.yml").resolve()
DOSSIER_PLAYBOOKS_GROUPES = RACINE / "playbooks/groupes"
FICHIER_DEPENDANCES = RACINE / "docs/dependances-groupes.yml"
FICHIER_NOMENCLATURE = INSTANCE / "plan/nomenclature.yml"

View file

@ -29,7 +29,7 @@ RACINE = Path(__file__).resolve().parents[1]
INSTANCE = Path(os.environ.get("SETOPS_INSTANCE") or (RACINE / "instance"))
FICHIER = INSTANCE / "plan/serveurs.yml"
FICHIER_NOMENCLATURE = INSTANCE / "plan/nomenclature.yml"
INVENTAIRE = RACINE / "inventories/production/hosts.yml"
INVENTAIRE = INSTANCE / "inventories/production/hosts.yml"
# Champs de placement / dimensionnement NON derivables (proviennent du plan).
CHAMPS_PLAN = [("noeud", "noeud"), ("stockage", "stockage"),