diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b7ba5b..2e69fdc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,14 @@ ## 2026-06-30 ### Ajouté +- **Inventaire d'instance neutre et configurable (`principal` / `SETOPS_INVENTAIRE`).** + Le moteur (Makefile + `inventory_gui`, `instancier`, `config_proxmox`, `serveurs`, + `applications`) ne code plus en dur `inventories/lab` / `inventories/production` : + il vise **un inventaire par instance**, détecté de façon **rétro-compatible** + (`principal` > `production` > `lab`) et surchargeable par `SETOPS_INVENTAIRE`. Les + instances existantes (découpage lab/production) continuent de fonctionner à + l'identique ; les nouvelles peuvent adopter `inventories/principal/`. Deuxième pierre + de la séparation **par instance** (la 1re étant le drapeau `setops_production`). - **Garde-fou de déploiement par instance (`setops_production`).** Le droit de déployer pour de vrai (GUI Vérifier/Déployer, `executer_flux`) ne dépend plus du *nom* de l'inventaire mais d'un drapeau explicite `setops_production` dans diff --git a/Makefile b/Makefile index e7ef39b..69a527e 100644 --- a/Makefile +++ b/Makefile @@ -6,9 +6,15 @@ export ANSIBLE_SSH_CONTROL_PATH_DIR ?= $(CURDIR)/.ansible/cp export ANSIBLE_SSH_ARGS ?= -F /dev/null -o ControlMaster=no export SETOPS_INSTANCE ?= instance -INVENTAIRE_LAB ?= $(SETOPS_INSTANCE)/inventories/lab/hosts.yml -INVENTAIRE_PRODUCTION ?= $(SETOPS_INSTANCE)/inventories/production/hosts.yml -FICHIER_INVENTAIRE ?= $(INVENTAIRE_PRODUCTION) +# Inventaire de l'instance : un seul par instance dans le modèle « séparation par +# instance ». Détection rétro-compatible : principal > production > lab. +# Surchargeable : make … SETOPS_INVENTAIRE=chemin/hosts.yml +SETOPS_INVENTAIRE ?= $(firstword $(wildcard $(SETOPS_INSTANCE)/inventories/principal/hosts.yml) $(wildcard $(SETOPS_INSTANCE)/inventories/production/hosts.yml) $(SETOPS_INSTANCE)/inventories/principal/hosts.yml) +export SETOPS_INVENTAIRE +# Inventaire « modèle » (construction du golden template) : lab > principal > production. +INVENTAIRE_LAB ?= $(firstword $(wildcard $(SETOPS_INSTANCE)/inventories/lab/hosts.yml) $(wildcard $(SETOPS_INSTANCE)/inventories/principal/hosts.yml) $(SETOPS_INSTANCE)/inventories/production/hosts.yml) +INVENTAIRE_PRODUCTION ?= $(SETOPS_INVENTAIRE) +FICHIER_INVENTAIRE ?= $(SETOPS_INVENTAIRE) FICHIER_DEPENDANCES ?= docs/dependances-groupes.yml GROUPE_MODELE ?= modeles_vm GROUPE_DEBIAN ?= serveur_debian diff --git a/scripts/applications.py b/scripts/applications.py index 2e1c515..c9fbab2 100644 --- a/scripts/applications.py +++ b/scripts/applications.py @@ -26,7 +26,18 @@ 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 = INSTANCE / "inventories/production/hosts.yml" +def _inventaire(instance: Path, *noms: str) -> Path: + forced = os.environ.get("SETOPS_INVENTAIRE") + if forced: + return Path(forced) + for nom in noms: + p = instance / "inventories" / nom / "hosts.yml" + if p.exists(): + return p + return instance / "inventories" / noms[0] / "hosts.yml" + + +INVENTAIRE = _inventaire(INSTANCE, "principal", "production") # Groupes 'serveurs_*' qui sont des capacites de SOCLE (sur toutes les VM), # pas des applications a part entiere. diff --git a/scripts/config_proxmox.py b/scripts/config_proxmox.py index 3cbbba8..e619b1d 100644 --- a/scripts/config_proxmox.py +++ b/scripts/config_proxmox.py @@ -14,10 +14,25 @@ import yaml RACINE = Path(__file__).resolve().parents[1] INSTANCE = Path(os.environ.get("SETOPS_INSTANCE") or (RACINE / "instance")) -FICHIER_PROXMOX = INSTANCE / "inventories/lab/group_vars/proxmox.yml" -# Voûte UNIQUE de l'environnement : tous les secrets (token Proxmox + vault_*) au -# même endroit, un seul mot de passe. Gabarit : exemples/vault.exemple.yml. -FICHIER_VAULT = INSTANCE / "inventories/lab/group_vars/all/vault.yml" + + +def _inventaire_dir(instance: Path, *noms: str) -> Path: + """Répertoire de l'inventaire modèle (porte la config Proxmox / le clonage).""" + forced = os.environ.get("SETOPS_INVENTAIRE") + if forced: + return Path(forced).parent + for nom in noms: + d = instance / "inventories" / nom + if (d / "hosts.yml").exists(): + return d + return instance / "inventories" / noms[0] + + +_INV_DIR = _inventaire_dir(INSTANCE, "lab", "principal", "production") +FICHIER_PROXMOX = _INV_DIR / "group_vars/proxmox.yml" +# Voûte UNIQUE de l'instance : tous les secrets (token Proxmox + vault_*) au même +# endroit, un seul mot de passe. Gabarit : exemples/vault.exemple.yml. +FICHIER_VAULT = _INV_DIR / "group_vars/all/vault.yml" GABARIT_VAULT = RACINE / "exemples/vault.exemple.yml" diff --git a/scripts/instancier.py b/scripts/instancier.py index 155a2a8..7cdf2a7 100644 --- a/scripts/instancier.py +++ b/scripts/instancier.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """Generateur d'inventaire Set-OPS (Phase 3) : plan -> hosts.yml. -NON destructif : ecrit dans instance/inventories/production/hosts.genere.yml et compare +NON destructif : ecrit hosts.genere.yml a cote de l'inventaire de l'instance et compare SEMANTIQUEMENT (via ansible-inventory --list) avec l'inventaire actuel. Aucune bascule tant que la comparaison n'est pas vide et validee. @@ -37,8 +37,21 @@ from inventory_rules import ( RACINE = Path(__file__).resolve().parents[1] ROLES = RACINE / "roles" INSTANCE = Path(os.environ.get("SETOPS_INSTANCE") or (RACINE / "instance")) -INVENTAIRE = INSTANCE / "inventories/production/hosts.yml" -GENERE = INSTANCE / "inventories/production/hosts.genere.yml" + + +def _inventaire(instance: Path, *noms: str) -> Path: + for nom in noms: + p = instance / "inventories" / nom / "hosts.yml" + if p.exists(): + return p + return instance / "inventories" / noms[0] / "hosts.yml" + + +# Inventaire unique de l'instance (rétro-compat : principal > production). +# SETOPS_INVENTAIRE force la cible. +_force = os.environ.get("SETOPS_INVENTAIRE") +INVENTAIRE = Path(_force) if _force else _inventaire(INSTANCE, "principal", "production") +GENERE = INVENTAIRE.with_name("hosts.genere.yml") FICHIER_SERVEURS = INSTANCE / "plan/serveurs.yml" FICHIER_APPLICATIONS = INSTANCE / "plan/applications.yml" FICHIER_NOMENCLATURE = INSTANCE / "plan/nomenclature.yml" diff --git a/scripts/inventory_gui.py b/scripts/inventory_gui.py index 05ebbf5..74e3c5d 100644 --- a/scripts/inventory_gui.py +++ b/scripts/inventory_gui.py @@ -38,8 +38,24 @@ from inventory_rules import ( RACINE = Path(__file__).resolve().parents[1] INSTANCE = Path(os.environ.get("SETOPS_INSTANCE") or (RACINE / "instance")) -INVENTAIRE_DEFAUT = INSTANCE / "inventories/production/hosts.yml" -INVENTAIRE_PRODUCTION = (INSTANCE / "inventories/production/hosts.yml").resolve() + + +def _inventaire(instance: Path, *noms: str) -> Path: + """Inventaire de l'instance, détection rétro-compatible (premier existant).""" + for nom in noms: + p = instance / "inventories" / nom / "hosts.yml" + if p.exists(): + return p + return instance / "inventories" / noms[0] / "hosts.yml" + + +# Un seul inventaire par instance (modèle « séparation par instance ») ; on accepte +# l'ancien découpage lab/production en rétro-compatibilité. SETOPS_INVENTAIRE force. +_force = os.environ.get("SETOPS_INVENTAIRE") +INVENTAIRE_DEFAUT = Path(_force) if _force else _inventaire(INSTANCE, "principal", "production") +INVENTAIRE_PRODUCTION = INVENTAIRE_DEFAUT.resolve() +# Inventaire qui porte la config Proxmox / le clonage (modèle) : lab > principal > production. +INVENTAIRE_MODELE = _inventaire(INSTANCE, "lab", "principal", "production") DOSSIER_PLAYBOOKS_GROUPES = RACINE / "playbooks/groupes" FICHIER_DEPENDANCES = RACINE / "docs/dependances-groupes.yml" FICHIER_NOMENCLATURE = INSTANCE / "plan/nomenclature.yml" @@ -49,11 +65,11 @@ FICHIER_DOMAINES = INSTANCE / "plan/domaines.yml" FICHIER_SERVEURS = INSTANCE / "plan/serveurs.yml" # Intrants de base (panneau GUI). Cibles d'ecriture possedees par le GUI. -# Identite : source UNIQUE partagee par tous les envs (symlink depuis chaque -# group_vars/all/). Proxmox : fichier lab (le clonage le lit en dur), cles gerees -# mises a jour, les autres cles preservees. Les SECRETS n'y entrent jamais. +# Identite : source UNIQUE partagee (symlink depuis group_vars/all/). Proxmox : +# group_vars de l'inventaire modele, cles gerees mises a jour, autres preservees. +# Les SECRETS n'y entrent jamais. INTRANTS_IDENTITE = INSTANCE / "inventories/partage/intrants-identite.yml" -INTRANTS_PROXMOX = INSTANCE / "inventories/lab/group_vars/proxmox.yml" +INTRANTS_PROXMOX = INVENTAIRE_MODELE.parent / "group_vars/proxmox.yml" FICHIERS_INTRANTS = {"identite": INTRANTS_IDENTITE, "proxmox": INTRANTS_PROXMOX} # (variable, fichier, classe, section, libelle, type) diff --git a/scripts/serveurs.py b/scripts/serveurs.py index 294a5f0..9c58cf2 100644 --- a/scripts/serveurs.py +++ b/scripts/serveurs.py @@ -29,7 +29,18 @@ 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 = INSTANCE / "inventories/production/hosts.yml" +def _inventaire(instance: Path, *noms: str) -> Path: + forced = os.environ.get("SETOPS_INVENTAIRE") + if forced: + return Path(forced) + for nom in noms: + p = instance / "inventories" / nom / "hosts.yml" + if p.exists(): + return p + return instance / "inventories" / noms[0] / "hosts.yml" + + +INVENTAIRE = _inventaire(INSTANCE, "principal", "production") # Champs de placement / dimensionnement NON derivables (proviennent du plan). CHAMPS_PLAN = [("noeud", "noeud"), ("stockage", "stockage"),