Bootstrap des applications depuis l'inventaire (prerequis Phase 3)

make applications-bootstrap : un groupe serveurs_* (hors socle debian/
durcis) sur un hote = une application. Les entrees existantes (pg-principal,
forge avec port/expose/requiert) sont preservees ; le reste est complete
depuis les appartenances de groupes de l'inventaire.

Resultat : 18 applications couvrant tous les services (multi-applis par VM
preservee : data-01 = postgresql+redis, obs-01 = grafana+loki+prometheus...).
Permettra au generateur de Phase 3 de deriver les groupes -> diff vide.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Daniel Allaire 2026-06-23 21:41:30 -04:00
parent 1aa3da8de5
commit 501ec38ab0
3 changed files with 91 additions and 1 deletions

View file

@ -376,7 +376,7 @@ inventaire-verifier: ansible-runtime
python3 scripts/bases_donnees.py verifier
python3 scripts/domaines.py verifier
.PHONY: bases bases-verifier domaines domaines-verifier applications applications-verifier serveurs serveurs-verifier serveurs-bootstrap
.PHONY: bases bases-verifier domaines domaines-verifier applications applications-verifier applications-bootstrap serveurs serveurs-verifier serveurs-bootstrap
serveurs:
python3 scripts/serveurs.py lister
@ -404,6 +404,9 @@ applications:
applications-verifier:
python3 scripts/applications.py verifier
applications-bootstrap:
python3 scripts/applications.py bootstrap
inventaire-lister: ansible-runtime
ansible-inventory -i $(FICHIER_INVENTAIRE) --list

View file

@ -3,9 +3,57 @@
# Une VM peut porter plusieurs applications ; une base se lie a une application.
---
applications:
collabora:
groupe: serveurs_collabora
hote: collab-01
nextcloud:
groupe: serveurs_nextcloud
hote: collab-01
pg-principal:
groupe: serveurs_postgresql
hote: data-01
redis:
groupe: serveurs_redis
hote: data-01
forge:
groupe: serveurs_forgejo
hote: forge-01
keycloak:
groupe: serveurs_keycloak
hote: idm-01
openldap:
groupe: serveurs_openldap
hote: idm-01
powerdns:
groupe: serveurs_powerdns
hote: infra-dns-01
nginx:
groupe: serveurs_nginx
hote: infra-edge-01
sendmail:
groupe: serveurs_sendmail
hote: infra-mail-01
step_ca:
groupe: serveurs_step_ca
hote: infra-pki-01
icinga:
groupe: serveurs_icinga
hote: mon-01
grafana:
groupe: serveurs_grafana
hote: obs-01
loki:
groupe: serveurs_loki
hote: obs-01
prometheus:
groupe: serveurs_prometheus
hote: obs-01
web_dorsaux:
groupe: serveurs_web_dorsaux
hote: web-dorsal-01
web_frontaux:
groupe: serveurs_web_frontaux
hote: web-frontal-01
web_frontaux-web-frontal-02:
groupe: serveurs_web_frontaux
hote: web-frontal-02

View file

@ -9,6 +9,7 @@ from pathlib import Path
import yaml
from inventory_gui import charger_yaml, liste_hotes
from inventory_rules import (
bases_de_application,
chaine_connexion,
@ -22,6 +23,11 @@ RACINE = Path(__file__).resolve().parents[1]
FICHIER = RACINE / "docs/applications.yml"
FICHIER_BASES = RACINE / "docs/bases-donnees.yml"
FICHIER_DOMAINES = RACINE / "docs/domaines.yml"
INVENTAIRE = RACINE / "inventories/production/hosts.yml"
# Groupes 'serveurs_*' qui sont des capacites de SOCLE (sur toutes les VM),
# pas des applications a part entiere.
GROUPES_SOCLE = {"serveurs_debian", "serveurs_durcis"}
def _liste(valeur: str) -> list:
@ -63,11 +69,39 @@ def lister(registre: dict, bases: dict) -> None:
print(f" DSN ({portee}/{base.get('usage', '-')}): {chaine_connexion(base, entree['serveur'])}")
def bootstrap(registre_existant: dict) -> dict:
"""(Re)genere les applications depuis les appartenances de groupes de l'inventaire.
Un groupe 'serveurs_*' (hors socle) sur un hote = une application. Les entrees
existantes correspondant a un couple (groupe, hote) toujours present sont
preservees telles quelles (port / expose / requiert ...).
"""
existant = registre_existant.get("applications") or {}
par_cle = {(a.get("groupe"), a.get("hote")): (cle, a) for cle, a in existant.items()}
apps: dict = {}
for h in liste_hotes(charger_yaml(INVENTAIRE)):
hote = h.get("nom")
if not hote:
continue
for groupe in sorted(h.get("groupes", [])):
if not groupe.startswith("serveurs_") or groupe in GROUPES_SOCLE:
continue
garde = par_cle.get((groupe, hote))
if garde:
apps[garde[0]] = garde[1]
continue
base = groupe[len("serveurs_"):]
app_id = base if base not in apps else f"{base}-{hote}"
apps[app_id] = {"groupe": groupe, "hote": hote}
return {"applications": apps}
def main() -> int:
parser = argparse.ArgumentParser(description="Registre des applications Set-OPS.")
sub = parser.add_subparsers(dest="commande", required=True)
sub.add_parser("lister", help="Affiche les applications et leurs DSN resolus.")
sub.add_parser("verifier", help="Valide la coherence du registre.")
sub.add_parser("bootstrap", help="(Re)genere applications.yml depuis les groupes de l'inventaire.")
pa = sub.add_parser("ajouter", help="Ajoute ou met a jour une application.")
pa.add_argument("--id", required=True)
@ -89,6 +123,11 @@ def main() -> int:
elif args.commande == "verifier":
valider_applications(registre, domaines)
print("Registre des applications valide.")
elif args.commande == "bootstrap":
registre = bootstrap(registre)
valider_applications(registre, domaines)
ecrire(registre)
print(f"docs/applications.yml genere depuis l'inventaire ({len(registre['applications'])} applications).")
elif args.commande == "ajouter":
app = {"groupe": args.groupe, "hote": args.hote}
if args.port is not None: