GUI : persister les journaux d'exécution

Chaque action (creer-vm/verifier/deployer) écrit un journal horodaté
<instance>/logs/<hote>-<action>-<date>.log en plus du streaming live.
Si le navigateur se déconnecte, l'exécution continue et le log est capturé
jusqu'au bout (au lieu d'être interrompue). node --check OK, serveur HTTP 200.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Daniel Allaire 2026-07-07 12:49:17 -04:00
parent 55847b6df0
commit b4ca67289a
2 changed files with 26 additions and 3 deletions

View file

@ -3,6 +3,11 @@
## 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,6 +12,7 @@ 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
@ -2053,6 +2054,7 @@ 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")
@ -2066,7 +2068,14 @@ 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()
self._ecrire_flux(f"$ make {cible} HOTE={hote}\n\n") # Persistance : un journal horodaté par exécution, en plus du streaming live.
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,
@ -2075,8 +2084,11 @@ 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):
proc.terminate() # Navigateur déconnecté : on continue à journaliser jusqu'au bout.
for reste in proc.stdout:
journal.write(reste)
break break
finally: finally:
rc = proc.wait() rc = proc.wait()
@ -2091,8 +2103,14 @@ class Gestionnaire(BaseHTTPRequestHandler):
marquer_serveur_actif(hote) marquer_serveur_actif(hote)
except Exception: except Exception:
pass pass
self._ecrire_flux(f"\n__FIN__ rc={rc}\n") journal.write(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)