Basculer : hosts.yml genere depuis le plan (diff vide)
make instancier-appliquer regenere inventories/production/hosts.yml depuis le plan (serveurs.yml + applications.yml + nomenclature.yml). Garde-fou : refuse si le diff n'est pas vide (FORCE=1 pour un changement intentionnel) ; git est le filet. Bascule effectuee avec diff vide verifie. hosts.yml est desormais un artefact genere, semantiquement identique a l'ancien (make inventaire-verifier complet OK, GUI charge 13 hotes). NOTE : a ce stade le GUI edite encore hosts.yml directement. Tant que le rework GUI n'est pas fait, editer le PLAN puis make instancier-appliquer. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
69dd730b70
commit
854e324348
4 changed files with 159 additions and 141 deletions
|
|
@ -3,6 +3,7 @@
|
|||
## 2026-06-23
|
||||
|
||||
### Modifié
|
||||
- **Bascule méta-classe (branche `bascule-inventaire-genere`).** `make instancier-appliquer` régénère `inventories/production/hosts.yml` **depuis le plan** (refuse si le diff n'est pas vide, sauf `FORCE=1` pour un changement intentionnel ; git sert de filet). Effectuée avec diff vide vérifié : `hosts.yml` est désormais un artefact généré, sémantiquement identique à l'ancien (vérifié par `make inventaire-verifier` complet + chargement GUI). À ce stade le GUI édite encore `hosts.yml` directement (rework à venir) : éditer le **plan** (`serveurs.yml`/`applications.yml`) puis `make instancier-appliquer`.
|
||||
- **Phase 2 (début) — vue Chaîne holistique.** Dans `make inventaire-ui`, la vue Chaîne montre désormais, pour chaque application d'un hôte, l'ensemble de ses liens : `expose` (FQDN publics), `requiert` (applications dont elle dépend) et ses bases (DSN), plus son port. On voit d'un coup d'œil comment VM, applications, bases et domaines s'articulent.
|
||||
- **Phase 1b — clarté de nommage.** La clé `domaines:` de `docs/nomenclature.yml` (familles fonctionnelles : `infra-pki`, `web-frontal`…) est renommée **`fonctions:`** pour lever la collision avec les domaines DNS (`docs/domaines.yml`). L'auto-proposition du GUI suit (`fonctionDe` / `infoFonction`, `nomenclature.fonctions`, libellés « Fonction inconnue… », placeholder `fonction-NN`). Aucun rôle ni inventaire impacté : la nomenclature n'est consommée qu'au moment de la planification.
|
||||
- **Refonte « application-hub », Phase 1 — les liens deviennent explicites (inventaire inchangé, zéro régression).** L'application porte désormais tous ses liens : `groupe` (capacité/rôle), `hote` (VM), `port`, **`requiert`** (autres applications dont elle dépend) et **`expose`** (FQDN publics qui la publient). L'**exposition DNS quitte `docs/domaines.yml`** (le bloc `exposition` est retiré) et vit sur l'application via `expose` ; `docs/domaines.yml` ne décrit plus que les zones (autorité, edge, secondaires, dnssec, mail). `serveurs_nginx` dérive ses vhosts des **applications** (`expose` + edge du domaine), résolvant `application → hôte → IP` (rôle + `expositions.conf.j2` réécrits). Nouvelle règle partagée `expositions_des_applications` (+ `domaine_parent`) dans `inventory_rules.py`, exposée à Ansible par le filter plugin ; `valider_applications` valide `requiert` (applis connues), `expose` (domaine parent déclaré) et `port`. CLI (`applications.py --port/--requiert/--expose`, `domaines.py` affiche les expositions dérivées) et **GUI** (champs Port/Requiert/Expose sur la fiche Application, persistés) suivent ; l'API expose aussi le registre des domaines. Le groupe n'est plus une cible de liaison, seulement une capacité. *(Renommage `nomenclature.domaines:` → `fonctions:` reporté en Phase 1b ; génération de l'inventaire depuis le plan en Phase 3.)*
|
||||
|
|
|
|||
5
Makefile
5
Makefile
|
|
@ -386,11 +386,14 @@ serveurs-verifier:
|
|||
serveurs-bootstrap:
|
||||
python3 scripts/serveurs.py bootstrap
|
||||
|
||||
.PHONY: instancier
|
||||
.PHONY: instancier instancier-appliquer
|
||||
instancier:
|
||||
python3 scripts/instancier.py generer
|
||||
python3 scripts/instancier.py comparer
|
||||
|
||||
instancier-appliquer:
|
||||
python3 scripts/instancier.py appliquer
|
||||
|
||||
bases:
|
||||
python3 scripts/bases_donnees.py lister
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,15 @@
|
|||
# Inventaire GENERE depuis le plan (make instancier / instancier-appliquer).
|
||||
# NE PAS editer a la main : edite docs/serveurs.yml + docs/applications.yml.
|
||||
# Source : docs/serveurs.yml + docs/applications.yml + docs/nomenclature.yml.
|
||||
all:
|
||||
children:
|
||||
modeles_vm:
|
||||
hosts: {}
|
||||
clients_dns:
|
||||
hosts:
|
||||
collab-01: null
|
||||
web-frontal-01: null
|
||||
clients_supervision:
|
||||
hosts:
|
||||
web-frontal-01: null
|
||||
hotes_actifs:
|
||||
hosts: {}
|
||||
hotes_planifies:
|
||||
|
|
@ -10,244 +18,227 @@ all:
|
|||
ansible_host: 10.1.15.21
|
||||
ansible_user: ansible
|
||||
proxmox_cidr: 24
|
||||
proxmox_passerelle: 10.1.15.1
|
||||
proxmox_vlan: 15
|
||||
proxmox_vmid: 95201
|
||||
proxmox_stockage: TrueNAS
|
||||
proxmox_coeurs: 2
|
||||
proxmox_disque_taille: 16G
|
||||
proxmox_memoire: 2048
|
||||
proxmox_coeurs: 2
|
||||
proxmox_passerelle: 10.1.15.1
|
||||
proxmox_stockage: TrueNAS
|
||||
proxmox_vlan: 15
|
||||
proxmox_vmid: 95201
|
||||
data-01:
|
||||
ansible_host: 10.1.13.11
|
||||
ansible_user: ansible
|
||||
proxmox_cidr: 24
|
||||
proxmox_passerelle: 10.1.13.1
|
||||
proxmox_vlan: 13
|
||||
proxmox_vmid: 93101
|
||||
proxmox_noeud: asgard
|
||||
proxmox_stockage: TrueNAS
|
||||
proxmox_coeurs: 4
|
||||
proxmox_disque_taille: 160G
|
||||
proxmox_memoire: 2048
|
||||
proxmox_coeurs: 4
|
||||
proxmox_noeud: asgard
|
||||
proxmox_passerelle: 10.1.13.1
|
||||
proxmox_stockage: TrueNAS
|
||||
proxmox_vlan: 13
|
||||
proxmox_vmid: 93101
|
||||
forge-01:
|
||||
ansible_host: 10.1.15.11
|
||||
ansible_user: ansible
|
||||
proxmox_cidr: 24
|
||||
proxmox_passerelle: 10.1.15.1
|
||||
proxmox_vlan: 15
|
||||
proxmox_vmid: 95101
|
||||
proxmox_stockage: TrueNAS
|
||||
proxmox_coeurs: 2
|
||||
proxmox_disque_taille: 16G
|
||||
proxmox_memoire: 2048
|
||||
proxmox_coeurs: 2
|
||||
proxmox_passerelle: 10.1.15.1
|
||||
proxmox_stockage: TrueNAS
|
||||
proxmox_vlan: 15
|
||||
proxmox_vmid: 95101
|
||||
idm-01:
|
||||
ansible_host: 10.1.12.11
|
||||
ansible_user: ansible
|
||||
proxmox_cidr: 24
|
||||
proxmox_passerelle: 10.1.12.1
|
||||
proxmox_vlan: 12
|
||||
proxmox_vmid: 92101
|
||||
proxmox_stockage: TrueNAS
|
||||
proxmox_coeurs: 2
|
||||
proxmox_disque_taille: 16G
|
||||
proxmox_memoire: 2048
|
||||
proxmox_coeurs: 2
|
||||
proxmox_passerelle: 10.1.12.1
|
||||
proxmox_stockage: TrueNAS
|
||||
proxmox_vlan: 12
|
||||
proxmox_vmid: 92101
|
||||
infra-dns-01:
|
||||
ansible_host: 10.1.11.41
|
||||
ansible_user: ansible
|
||||
proxmox_cidr: 24
|
||||
proxmox_passerelle: 10.1.11.1
|
||||
proxmox_vlan: 11
|
||||
proxmox_vmid: 91401
|
||||
proxmox_stockage: TrueNAS
|
||||
proxmox_coeurs: 2
|
||||
proxmox_disque_taille: 16G
|
||||
proxmox_memoire: 2048
|
||||
proxmox_coeurs: 2
|
||||
proxmox_passerelle: 10.1.11.1
|
||||
proxmox_stockage: TrueNAS
|
||||
proxmox_vlan: 11
|
||||
proxmox_vmid: 91401
|
||||
infra-edge-01:
|
||||
ansible_host: 10.1.11.21
|
||||
ansible_user: ansible
|
||||
proxmox_cidr: 24
|
||||
proxmox_passerelle: 10.1.11.1
|
||||
proxmox_vlan: 11
|
||||
proxmox_vmid: 91201
|
||||
proxmox_stockage: TrueNAS
|
||||
proxmox_coeurs: 2
|
||||
proxmox_disque_taille: 16G
|
||||
proxmox_memoire: 2048
|
||||
proxmox_coeurs: 2
|
||||
proxmox_passerelle: 10.1.11.1
|
||||
proxmox_stockage: TrueNAS
|
||||
proxmox_vlan: 11
|
||||
proxmox_vmid: 91201
|
||||
infra-mail-01:
|
||||
ansible_host: 10.1.11.31
|
||||
ansible_user: ansible
|
||||
proxmox_cidr: 24
|
||||
proxmox_passerelle: 10.1.11.1
|
||||
proxmox_vlan: 11
|
||||
proxmox_vmid: 91301
|
||||
proxmox_stockage: TrueNAS
|
||||
proxmox_coeurs: 2
|
||||
proxmox_disque_taille: 16G
|
||||
proxmox_memoire: 2048
|
||||
proxmox_coeurs: 2
|
||||
proxmox_passerelle: 10.1.11.1
|
||||
proxmox_stockage: TrueNAS
|
||||
proxmox_vlan: 11
|
||||
proxmox_vmid: 91301
|
||||
infra-pki-01:
|
||||
ansible_host: 10.1.11.11
|
||||
ansible_user: ansible
|
||||
proxmox_cidr: 24
|
||||
proxmox_passerelle: 10.1.11.1
|
||||
proxmox_vlan: 11
|
||||
proxmox_vmid: 91101
|
||||
proxmox_stockage: TrueNAS
|
||||
proxmox_coeurs: 2
|
||||
proxmox_disque_taille: 16G
|
||||
proxmox_memoire: 2048
|
||||
proxmox_coeurs: 2
|
||||
proxmox_passerelle: 10.1.11.1
|
||||
proxmox_stockage: TrueNAS
|
||||
proxmox_vlan: 11
|
||||
proxmox_vmid: 91101
|
||||
mon-01:
|
||||
ansible_host: 10.1.14.41
|
||||
ansible_user: ansible
|
||||
proxmox_cidr: 24
|
||||
proxmox_passerelle: 10.1.14.1
|
||||
proxmox_vlan: 14
|
||||
proxmox_vmid: 94401
|
||||
proxmox_stockage: TrueNAS
|
||||
proxmox_coeurs: 2
|
||||
proxmox_disque_taille: 16G
|
||||
proxmox_memoire: 2048
|
||||
proxmox_coeurs: 2
|
||||
proxmox_passerelle: 10.1.14.1
|
||||
proxmox_stockage: TrueNAS
|
||||
proxmox_vlan: 14
|
||||
proxmox_vmid: 94401
|
||||
obs-01:
|
||||
ansible_host: 10.1.14.11
|
||||
ansible_user: ansible
|
||||
proxmox_cidr: 24
|
||||
proxmox_passerelle: 10.1.14.1
|
||||
proxmox_vlan: 14
|
||||
proxmox_vmid: 94101
|
||||
proxmox_stockage: TrueNAS
|
||||
proxmox_coeurs: 4
|
||||
proxmox_disque_taille: 16G
|
||||
proxmox_memoire: 2048
|
||||
proxmox_coeurs: 4
|
||||
proxmox_passerelle: 10.1.14.1
|
||||
proxmox_stockage: TrueNAS
|
||||
proxmox_vlan: 14
|
||||
proxmox_vmid: 94101
|
||||
web-dorsal-01:
|
||||
ansible_host: 10.1.15.41
|
||||
ansible_user: ansible
|
||||
proxmox_cidr: 24
|
||||
proxmox_passerelle: 10.1.15.1
|
||||
proxmox_vlan: 15
|
||||
proxmox_vmid: 95401
|
||||
proxmox_stockage: TrueNAS
|
||||
proxmox_coeurs: 2
|
||||
proxmox_disque_taille: 16G
|
||||
proxmox_memoire: 2048
|
||||
proxmox_coeurs: 2
|
||||
proxmox_passerelle: 10.1.15.1
|
||||
proxmox_stockage: TrueNAS
|
||||
proxmox_vlan: 15
|
||||
proxmox_vmid: 95401
|
||||
web-frontal-01:
|
||||
ansible_host: 10.1.15.31
|
||||
ansible_user: ansible
|
||||
proxmox_cidr: 24
|
||||
proxmox_passerelle: 10.1.15.1
|
||||
proxmox_vlan: 15
|
||||
proxmox_vmid: 95301
|
||||
proxmox_stockage: TrueNAS
|
||||
proxmox_coeurs: 2
|
||||
proxmox_disque_taille: 16G
|
||||
proxmox_memoire: 2048
|
||||
proxmox_coeurs: 2
|
||||
proxmox_passerelle: 10.1.15.1
|
||||
proxmox_stockage: TrueNAS
|
||||
proxmox_vlan: 15
|
||||
proxmox_vmid: 95301
|
||||
web-frontal-02:
|
||||
ansible_host: 10.1.15.32
|
||||
ansible_user: ansible
|
||||
proxmox_cidr: 24
|
||||
proxmox_passerelle: 10.1.15.1
|
||||
proxmox_vlan: 15
|
||||
proxmox_vmid: 95302
|
||||
proxmox_stockage: TrueNAS
|
||||
proxmox_coeurs: 2
|
||||
proxmox_disque_taille: 16G
|
||||
proxmox_memoire: 2048
|
||||
proxmox_coeurs: 2
|
||||
clients_dns:
|
||||
hosts:
|
||||
collab-01: {}
|
||||
web-frontal-01: {}
|
||||
clients_journaux:
|
||||
proxmox_passerelle: 10.1.15.1
|
||||
proxmox_stockage: TrueNAS
|
||||
proxmox_vlan: 15
|
||||
proxmox_vmid: 95302
|
||||
modeles_vm:
|
||||
hosts: {}
|
||||
clients_ldap:
|
||||
hosts: {}
|
||||
clients_metriques:
|
||||
hosts: {}
|
||||
clients_pki:
|
||||
hosts: {}
|
||||
clients_smtp:
|
||||
hosts: {}
|
||||
clients_supervision:
|
||||
hosts:
|
||||
web-frontal-01: {}
|
||||
serveurs_collabora:
|
||||
hosts:
|
||||
collab-01: {}
|
||||
collab-01: null
|
||||
serveurs_debian:
|
||||
hosts:
|
||||
collab-01: {}
|
||||
data-01: {}
|
||||
forge-01: {}
|
||||
idm-01: {}
|
||||
infra-dns-01: {}
|
||||
infra-edge-01: {}
|
||||
infra-mail-01: {}
|
||||
infra-pki-01: {}
|
||||
mon-01: {}
|
||||
obs-01: {}
|
||||
web-dorsal-01: {}
|
||||
web-frontal-01: {}
|
||||
web-frontal-02: {}
|
||||
collab-01: null
|
||||
data-01: null
|
||||
forge-01: null
|
||||
idm-01: null
|
||||
infra-dns-01: null
|
||||
infra-edge-01: null
|
||||
infra-mail-01: null
|
||||
infra-pki-01: null
|
||||
mon-01: null
|
||||
obs-01: null
|
||||
web-dorsal-01: null
|
||||
web-frontal-01: null
|
||||
web-frontal-02: null
|
||||
serveurs_durcis:
|
||||
hosts:
|
||||
collab-01: {}
|
||||
data-01: {}
|
||||
forge-01: {}
|
||||
idm-01: {}
|
||||
infra-dns-01: {}
|
||||
infra-edge-01: {}
|
||||
infra-mail-01: {}
|
||||
infra-pki-01: {}
|
||||
mon-01: {}
|
||||
obs-01: {}
|
||||
web-dorsal-01: {}
|
||||
web-frontal-01: {}
|
||||
web-frontal-02: {}
|
||||
collab-01: null
|
||||
data-01: null
|
||||
forge-01: null
|
||||
idm-01: null
|
||||
infra-dns-01: null
|
||||
infra-edge-01: null
|
||||
infra-mail-01: null
|
||||
infra-pki-01: null
|
||||
mon-01: null
|
||||
obs-01: null
|
||||
web-dorsal-01: null
|
||||
web-frontal-01: null
|
||||
web-frontal-02: null
|
||||
serveurs_forgejo:
|
||||
hosts:
|
||||
forge-01: {}
|
||||
forge-01: null
|
||||
serveurs_grafana:
|
||||
hosts:
|
||||
obs-01: {}
|
||||
obs-01: null
|
||||
serveurs_icinga:
|
||||
hosts:
|
||||
mon-01: {}
|
||||
mon-01: null
|
||||
serveurs_keycloak:
|
||||
hosts:
|
||||
idm-01: {}
|
||||
idm-01: null
|
||||
serveurs_loki:
|
||||
hosts:
|
||||
obs-01: {}
|
||||
obs-01: null
|
||||
serveurs_nextcloud:
|
||||
hosts:
|
||||
collab-01: {}
|
||||
collab-01: null
|
||||
serveurs_nginx:
|
||||
hosts:
|
||||
infra-edge-01: {}
|
||||
infra-edge-01: null
|
||||
serveurs_openldap:
|
||||
hosts:
|
||||
idm-01: {}
|
||||
idm-01: null
|
||||
serveurs_postgresql:
|
||||
hosts:
|
||||
data-01: {}
|
||||
data-01: null
|
||||
serveurs_powerdns:
|
||||
hosts:
|
||||
infra-dns-01: {}
|
||||
infra-dns-01: null
|
||||
serveurs_prometheus:
|
||||
hosts:
|
||||
obs-01: {}
|
||||
obs-01: null
|
||||
serveurs_redis:
|
||||
hosts:
|
||||
data-01: {}
|
||||
data-01: null
|
||||
serveurs_sendmail:
|
||||
hosts:
|
||||
infra-mail-01: {}
|
||||
infra-mail-01: null
|
||||
serveurs_step_ca:
|
||||
hosts:
|
||||
infra-pki-01: {}
|
||||
infra-pki-01: null
|
||||
serveurs_web_dorsaux:
|
||||
hosts:
|
||||
web-dorsal-01: {}
|
||||
web-dorsal-01: null
|
||||
serveurs_web_frontaux:
|
||||
hosts:
|
||||
web-frontal-01: {}
|
||||
web-frontal-02: {}
|
||||
hotes_proxmox:
|
||||
hosts: {}
|
||||
web-frontal-01: null
|
||||
web-frontal-02: null
|
||||
|
|
|
|||
|
|
@ -80,10 +80,11 @@ def generer() -> dict:
|
|||
return {"all": {"children": children}}
|
||||
|
||||
|
||||
def ecrire() -> None:
|
||||
entete = ("# Inventaire GENERE depuis le plan (make instancier). NE PAS editer a la main.\n"
|
||||
def ecrire(path: Path = GENERE) -> None:
|
||||
entete = ("# Inventaire GENERE depuis le plan (make instancier / instancier-appliquer).\n"
|
||||
"# NE PAS editer a la main : edite docs/serveurs.yml + docs/applications.yml.\n"
|
||||
"# Source : docs/serveurs.yml + docs/applications.yml + docs/nomenclature.yml.\n")
|
||||
with GENERE.open("w", encoding="utf-8") as fichier:
|
||||
with path.open("w", encoding="utf-8") as fichier:
|
||||
fichier.write(entete)
|
||||
yaml.safe_dump(generer(), fichier, default_flow_style=False, sort_keys=True, allow_unicode=True)
|
||||
|
||||
|
|
@ -102,11 +103,9 @@ def _resolu(fichier: Path) -> tuple[dict, dict]:
|
|||
return hostvars, groupes
|
||||
|
||||
|
||||
def comparer() -> int:
|
||||
if not GENERE.exists():
|
||||
ecrire()
|
||||
hv_act, grp_act = _resolu(INVENTAIRE)
|
||||
hv_gen, grp_gen = _resolu(GENERE)
|
||||
def compter_ecarts(reference: Path, genere: Path) -> int:
|
||||
hv_act, grp_act = _resolu(reference)
|
||||
hv_gen, grp_gen = _resolu(genere)
|
||||
hotes = sorted(set(hv_act) | set(hv_gen))
|
||||
ecarts = 0
|
||||
for hote in hotes:
|
||||
|
|
@ -128,10 +127,30 @@ def comparer() -> int:
|
|||
if vars_diff:
|
||||
details = ", ".join(f"{k} (inv={hv_act[hote].get(k)!r} gen={hv_gen[hote].get(k)!r})" for k in sorted(vars_diff))
|
||||
print(f" vars differentes : {details}")
|
||||
return ecarts
|
||||
|
||||
|
||||
def comparer() -> int:
|
||||
ecrire(GENERE)
|
||||
ecarts = compter_ecarts(INVENTAIRE, GENERE)
|
||||
if ecarts == 0:
|
||||
print("DIFF VIDE : le plan reproduit exactement l'inventaire actuel. Bascule possible.")
|
||||
else:
|
||||
print(f"\n{ecarts} hote(s) avec ecart. Bascule INTERDITE tant que ce n'est pas vide.")
|
||||
print(f"\n{ecarts} hote(s) avec ecart.")
|
||||
return 0
|
||||
|
||||
|
||||
def appliquer(force: bool = False) -> int:
|
||||
"""Ecrit hosts.yml depuis le plan. Refuse si le diff n'est pas vide (sauf --force)."""
|
||||
ecrire(GENERE)
|
||||
ecarts = compter_ecarts(INVENTAIRE, GENERE)
|
||||
if ecarts and not force:
|
||||
print(f"\nDiff NON vide ({ecarts} hote(s)). Revois 'make instancier'. "
|
||||
"Utilise FORCE=1 pour appliquer un changement intentionnel du plan.")
|
||||
return 1
|
||||
ecrire(INVENTAIRE)
|
||||
print(f"\n{INVENTAIRE.relative_to(RACINE)} (re)genere depuis le plan. "
|
||||
"git est le filet : git diff / git checkout pour revenir.")
|
||||
return 0
|
||||
|
||||
|
||||
|
|
@ -139,8 +158,10 @@ def main() -> int:
|
|||
import argparse
|
||||
parser = argparse.ArgumentParser(description="Generateur d'inventaire Set-OPS (Phase 3).")
|
||||
sub = parser.add_subparsers(dest="commande", required=True)
|
||||
sub.add_parser("generer", help="Ecrit hosts.yml.genere depuis le plan.")
|
||||
sub.add_parser("generer", help="Ecrit hosts.genere.yml depuis le plan.")
|
||||
sub.add_parser("comparer", help="Compare le genere a l'inventaire actuel (semantique).")
|
||||
pa = sub.add_parser("appliquer", help="Ecrit hosts.yml depuis le plan (refuse si diff non vide).")
|
||||
pa.add_argument("--force", action="store_true", help="Applique meme si le diff n'est pas vide.")
|
||||
args = parser.parse_args()
|
||||
try:
|
||||
if args.commande == "generer":
|
||||
|
|
@ -148,6 +169,8 @@ def main() -> int:
|
|||
print(f"{GENERE.relative_to(RACINE)} genere depuis le plan.")
|
||||
elif args.commande == "comparer":
|
||||
return comparer()
|
||||
elif args.commande == "appliquer":
|
||||
return appliquer(force=args.force)
|
||||
except Exception as exc:
|
||||
print(f"erreur: {exc}", file=sys.stderr)
|
||||
return 2
|
||||
|
|
|
|||
Reference in a new issue