Compare commits

..

No commits in common. "b4ca67289a9d3a2e7356ed12dcd0bf3a7b793264" and "b5e2edd3de2394711e49540839faf0261db1b037" have entirely different histories.

2 changed files with 6 additions and 43 deletions

View file

@ -3,11 +3,6 @@
## 2026-07-07 (soir) ## 2026-07-07 (soir)
### Ajouté ### Ajouté
- **GUI : persistance des journaux d'exécution.** Chaque action lancée depuis l'interface
(créer-vm / vérifier / déployer) écrit désormais, **en plus du streaming live**, un journal
horodaté `<instance>/logs/<hôte>-<action>-<date>.log` (gitignoré). Bonus : si le navigateur se
déconnecte, l'exécution **continue** et le journal est capturé jusqu'au bout (au lieu d'être
interrompue) — utile pour un déploiement qu'on ne veut pas voir avorter à la fermeture d'un onglet.
- **GUI : vues « Flux » et « Couches » (lecture seule).** Deux nouveaux onglets rendent visibles - **GUI : vues « Flux » et « Couches » (lecture seule).** Deux nouveaux onglets rendent visibles
dans l'interface deux registres jusque-là en ligne de commande seulement : **Flux** = la matrice dans l'interface deux registres jusque-là en ligne de commande seulement : **Flux** = la matrice
d'audit réseau (rôle · sens · port · pair · chiffrement coloré · raison, depuis les `meta/flux.yml`), d'audit réseau (rôle · sens · port · pair · chiffrement coloré · raison, depuis les `meta/flux.yml`),

View file

@ -12,7 +12,6 @@ import secrets
import socket import socket
import subprocess import subprocess
import tempfile import tempfile
import time
import threading import threading
from concurrent.futures import ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor
from pathlib import Path from pathlib import Path
@ -102,26 +101,12 @@ INTRANTS_CLES_INTERDITES = {
} }
# Rappel en lecture seule (jamais de valeur) : secrets a fournir via Ansible Vault. # Rappel en lecture seule (jamais de valeur) : secrets a fournir via Ansible Vault.
SECRETS_ATTENDUS = [ SECRETS_ATTENDUS = [
# Accès Proxmox (proxmox.vault.yml)
"proxmox_api_token_id", "proxmox_api_token_secret", "proxmox_api_token_id", "proxmox_api_token_secret",
# PKI (step-ca)
"vault_step_ca_password", "vault_step_ca_provisioner_password", "vault_step_ca_password", "vault_step_ca_provisioner_password",
# Identité (LDAP / Keycloak)
"vault_openldap_admin", "vault_ldap_sssd", "vault_keycloak_admin", "vault_openldap_admin", "vault_ldap_sssd", "vault_keycloak_admin",
# Bases de données "vault_postgresql_keycloak", "vault_bd_keycloak", "vault_forgejo_admin",
"vault_postgresql_keycloak", "vault_bd_keycloak", "vault_bd_forgejo", "vault_forgejo_secret_key", "vault_forgejo_internal_token", "vault_bd_forgejo",
"vault_bd_icingadb", "vault_bd_nextcloud", "vault_grafana_admin", "vault_redis", "vault_bd_icingadb",
# Forge (Forgejo)
"vault_forgejo_admin", "vault_forgejo_secret_key", "vault_forgejo_internal_token",
# Observabilité / cache
"vault_grafana_admin", "vault_redis",
# Applications (admin)
"vault_nextcloud_admin",
# Secrets OIDC des clients SSO (un par app fédérée)
"vault_grafana_oidc", "vault_forgejo_oidc", "vault_icingaweb2_oidc",
"vault_nextcloud_oidc", "vault_oauth2_cookie",
# Sauvegarde (restic hors-nœud)
"vault_restic_password", "vault_backup_ssh_privkey",
] ]
# Garde-fous des executions (verifier / deployer) depuis l'interface. # Garde-fous des executions (verifier / deployer) depuis l'interface.
@ -2054,7 +2039,6 @@ class Gestionnaire(BaseHTTPRequestHandler):
self.repondre_json(409, {"erreur": "Une execution est deja en cours."}) self.repondre_json(409, {"erreur": "Une execution est deja en cours."})
return return
fichier_vault = None fichier_vault = None
journal = None
try: try:
cible = {"deployer": "deployer", "pousser": "deployer", cible = {"deployer": "deployer", "pousser": "deployer",
"creer": "creer-vm"}.get(mode, "verifier-deploiement") "creer": "creer-vm"}.get(mode, "verifier-deploiement")
@ -2068,14 +2052,7 @@ class Gestionnaire(BaseHTTPRequestHandler):
self.send_header("Content-Type", "text/plain; charset=utf-8") self.send_header("Content-Type", "text/plain; charset=utf-8")
self.send_header("Cache-Control", "no-cache") self.send_header("Cache-Control", "no-cache")
self.end_headers() self.end_headers()
# Persistance : un journal horodaté par exécution, en plus du streaming live. self._ecrire_flux(f"$ make {cible} HOTE={hote}\n\n")
dossier_logs = self.inventaire.parents[2] / "logs"
dossier_logs.mkdir(parents=True, exist_ok=True)
chemin_log = dossier_logs / f"{hote}-{cible}-{time.strftime('%Y%m%dT%H%M%S')}.log"
journal = open(chemin_log, "w", encoding="utf-8")
entete = f"$ make {cible} HOTE={hote}\n\n"
journal.write(entete)
self._ecrire_flux(entete)
proc = subprocess.Popen( proc = subprocess.Popen(
["make", cible, f"HOTE={hote}"], ["make", cible, f"HOTE={hote}"],
cwd=str(RACINE), env=env, cwd=str(RACINE), env=env,
@ -2084,11 +2061,8 @@ class Gestionnaire(BaseHTTPRequestHandler):
) )
try: try:
for ligne in proc.stdout: for ligne in proc.stdout:
journal.write(ligne)
if not self._ecrire_flux(ligne): if not self._ecrire_flux(ligne):
# Navigateur déconnecté : on continue à journaliser jusqu'au bout. proc.terminate()
for reste in proc.stdout:
journal.write(reste)
break break
finally: finally:
rc = proc.wait() rc = proc.wait()
@ -2103,14 +2077,8 @@ class Gestionnaire(BaseHTTPRequestHandler):
marquer_serveur_actif(hote) marquer_serveur_actif(hote)
except Exception: except Exception:
pass pass
journal.write(f"\n__FIN__ rc={rc}\n") self._ecrire_flux(f"\n__FIN__ rc={rc}\n")
self._ecrire_flux(f"\n[journal : {os.path.relpath(chemin_log, RACINE)}]\n__FIN__ rc={rc}\n")
finally: finally:
if journal:
try:
journal.close()
except OSError:
pass
if fichier_vault: if fichier_vault:
try: try:
os.remove(fichier_vault) os.remove(fichier_vault)