Set-OPS-Public/scripts/bases_donnees.py
Daniel Allaire c18debc25e
Some checks are pending
verifier / verifier (push) Waiting to run
resolution d'instance : une seule, partagee — au lieu de neuf copies
Cinq jours, cinq defauts, tous de la meme famille : « quelle instance, quel inventaire ? »
Neuf modules portaient chacun leur reponse.

- 18 aout : P03 comparait chaque instance a l'inventaire d'une AUTRE ;
- 19 aout : verifier_ports codait `principal/` en dur ; verifier_intrants et
  _frontiere_absente lisaient le symlink au lieu de la variable ;
- 20 aout : devis_placement rendait un verdict juste sur le mauvais tenant ;
- 22 aout : P35, puis P36 — la dixieme, trouvee par la preuve elle-meme.

Aucune n'etait une faute d'inattention : chacune avait ete ecrite de bonne foi, a un
moment ou le besoin semblait local. C'est le mode de panne de la duplication — pas
l'erreur, mais la DERIVE, invisible depuis l'interieur d'un fichier.

LA RESOLUTION UNIQUE. `inventory_rules` porte instance_courante(), inventaire_de(),
dossier_inventaire() et plan_de(). Trois niveaux de repli, dont le TROISIEME manquait a la
moitie des copies : un hosts.yml existant, puis un REPERTOIRE existant (instance neuve —
c'est ce qui faisait echouer `make instancier` sur le modele public), puis le defaut.
Vingt-huit modules y sont branches.

CE QUI REND CE REFACTOR SUR : avant de toucher quoi que ce soit, chaque module a ete
interroge sur ce qu'il resolvait, pour les DEUX ecosystemes. Apres refactor, meme mesure :
17 modules x 2 instances, diff VIDE. Aucune resolution n'a change — prouve, pas suppose.

P41 echoue des qu'un module reintroduit une copie. Eprouvee en negatif : une copie
replacee dans genome.py est signalee avec son numero de ligne. Trois exemptions nommees :
instances.py et inventory_gui.py manipulent le SYMLINK lui-meme (bascule d'instance), et
devis_opnsense lit deliberement quelle instance est ACTIVE. Elles parlent du lien, pas de
la resolution.

make verifier 41 OK, 0 echec, 0 saute ; make ci idem ; lint vert.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 14:19:55 -04:00

131 lines
5.3 KiB
Python

#!/usr/bin/env python3
"""Gere le registre des bases de donnees Set-OPS (CLI, miroir de make inventaire-ui)."""
from __future__ import annotations
import os
import argparse
import sys
from pathlib import Path
import yaml
from inventory_rules import (
instance_courante,
chaine_connexion,
charger_applications,
charger_bases_donnees,
valider_bases,
)
RACINE = Path(__file__).resolve().parents[1]
INSTANCE = instance_courante()
FICHIER = INSTANCE / "plan/bases-donnees.yml"
FICHIER_APPLICATIONS = INSTANCE / "plan/applications.yml"
def ecrire(registre: dict) -> None:
entete = (
"# Registre des bases de donnees Set-OPS (serveurs_bd + bases_donnees).\n"
"# Edite par make inventaire-ui ou scripts/bases_donnees.py.\n"
"# 'portee' (application|groupe|hote) interprete 'consommateur' ; defaut groupe.\n"
"# 'secret' nomme une variable Ansible Vault (jamais le mot de passe).\n"
"---\n"
)
contenu = {
"serveurs_bd": registre.get("serveurs_bd", {}) or {},
"bases_donnees": registre.get("bases_donnees", {}) or {},
}
with FICHIER.open("w", encoding="utf-8") as fichier:
fichier.write(entete)
yaml.safe_dump(contenu, fichier, default_flow_style=False, sort_keys=False, allow_unicode=True)
def lister(registre: dict) -> None:
serveurs = registre.get("serveurs_bd", {})
print("Serveurs de bases de donnees:")
for nom, srv in serveurs.items():
print(f" {nom}: {srv.get('type')} {srv.get('hote')}:{srv.get('port')} (groupe {srv.get('groupe', '-')})")
print("Bases applicatives:")
for cle, base in registre.get("bases_donnees", {}).items():
srv = serveurs.get(base.get("serveur"), {})
conso = base.get("consommateur", "?")
portee = base.get("portee", "groupe")
usage = base.get("usage", "-")
print(f" {cle} <- {conso} [{portee}/{usage}]: {chaine_connexion(base, srv)} [secret {base.get('secret')}]")
def main() -> int:
parser = argparse.ArgumentParser(description="Registre des bases de donnees Set-OPS.")
sub = parser.add_subparsers(dest="commande", required=True)
sub.add_parser("lister", help="Affiche serveurs, bases et chaines de connexion.")
sub.add_parser("verifier", help="Valide la coherence du registre.")
ps = sub.add_parser("ajouter-serveur", help="Ajoute ou met a jour un serveur de BD.")
ps.add_argument("--nom", required=True)
ps.add_argument("--type", default="postgres")
ps.add_argument("--hote", required=True)
ps.add_argument("--port", type=int, default=5432)
ps.add_argument("--groupe", default="")
pb = sub.add_parser("ajouter-base", help="Ajoute ou met a jour une base applicative.")
pb.add_argument("--cle", required=True)
pb.add_argument("--serveur", required=True)
pb.add_argument("--base", required=True)
pb.add_argument("--proprietaire", required=True)
pb.add_argument("--secret", required=True)
pb.add_argument("--consommateur", required=True,
help="Qui utilise la base : groupe, hote ou id d'application (selon --portee).")
pb.add_argument("--portee", default="groupe", choices=["groupe", "hote", "application"])
pb.add_argument("--usage", default="principale")
pr = sub.add_parser("retirer-base", help="Retire une base applicative.")
pr.add_argument("--cle", required=True)
prs = sub.add_parser("retirer-serveur", help="Retire un serveur de BD.")
prs.add_argument("--nom", required=True)
args = parser.parse_args()
try:
registre = charger_bases_donnees(FICHIER)
applications = charger_applications(FICHIER_APPLICATIONS)
if args.commande == "lister":
lister(registre)
elif args.commande == "verifier":
valider_bases(registre, applications)
print("Registre des bases valide.")
elif args.commande == "ajouter-serveur":
registre["serveurs_bd"][args.nom] = {
"type": args.type, "hote": args.hote, "port": args.port,
**({"groupe": args.groupe} if args.groupe else {}),
}
valider_bases(registre, applications)
ecrire(registre)
print(f"Serveur de BD '{args.nom}' enregistre.")
elif args.commande == "ajouter-base":
registre["bases_donnees"][args.cle] = {
"serveur": args.serveur, "base": args.base,
"proprietaire": args.proprietaire, "secret": args.secret,
"consommateur": args.consommateur, "portee": args.portee, "usage": args.usage,
}
valider_bases(registre, applications)
ecrire(registre)
print(f"Base '{args.cle}' enregistree.")
elif args.commande == "retirer-base":
registre["bases_donnees"].pop(args.cle, None)
valider_bases(registre, applications)
ecrire(registre)
print(f"Base '{args.cle}' retiree.")
elif args.commande == "retirer-serveur":
registre["serveurs_bd"].pop(args.nom, None)
valider_bases(registre, applications)
ecrire(registre)
print(f"Serveur de BD '{args.nom}' retire.")
except Exception as exc:
print(f"erreur: {exc}", file=sys.stderr)
return 2
return 0
if __name__ == "__main__":
raise SystemExit(main())