[IMP] script todo: suffixe d'arch dans le nom + log d'install non vide

1) Nommage : _qemu_infra_name ajoute un suffixe d'architecture quand elle
   diffère de la native de l'hôte -> déployer un s390x sur un hôte amd64
   donne « erplibre-ubuntu-2604-s390x » (au lieu de « erplibre-ubuntu-2604 »).
   Évite les collisions de noms entre archis et rend l'archi visible. Passé
   dans les 3 appelants (VM unique + plan + jobs de l'infra).

2) Log d'installation vide : sur une archi ÉMULÉE (s390x/arm64 sur x86) le
   boot prend plusieurs minutes ; pendant ce temps l'attente n'écrivait rien
   -> le log restait VIDE et paraissait « bloqué ». _launch_one écrit
   désormais un en-tête d'attente immédiat + un battement toutes les ~30 s,
   et l'attente sshd/cloud-init passe de ~12 à ~20 min (boot émulé lent).
   _qemu_wait_ssh (chemin streamé) : timeout porté à 20 min également.

Validé : nommage (amd64/natif sans suffixe, s390x/arm64 suffixés) ; le log
se remplit dès le départ (en-tête + « ... 30s »).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Mathieu Benoit 2026-07-28 08:15:46 +00:00
parent 88dc1d5409
commit 0930f30cd3
3 changed files with 51 additions and 13 deletions

View file

@ -20,6 +20,14 @@ import subprocess
import time
from pathlib import Path
try:
from script.todo.todo_i18n import t
except Exception: # pragma: no cover - repli si i18n indisponible
def t(key: str) -> str:
return key
EXIT_MARKER = "__ERPLIBRE_EXIT__"
SSH_OPTS = (
"-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "
@ -38,7 +46,8 @@ def _launch_one(ip: str, remote_cmd: str, log_path: str) -> None:
"""Lance une install SSH DÉTACHÉE : attend le sshd, exécute, journalise
la sortie puis écrit le marqueur de fin avec le code de sortie."""
# Sonde de disponibilité : on attend que sshd réponde ET que cloud-init
# soit TERMINÉ, via des connexions COURTES successives (jusqu'à ~12 min).
# soit TERMINÉ, via des connexions COURTES successives (jusqu'à ~20 min :
# une architecture ÉMULÉE, s390x/arm64 sur hôte x86, boote lentement).
# Au 1er boot, cloud-init régénère les clés d'hôte et REDÉMARRE sshd : une
# session SSH longue (ex. « cloud-init status --wait ») serait alors tuée
# (« Connection closed by remote host », exit 255 — cas Fedora). Chaque
@ -49,16 +58,27 @@ def _launch_one(ip: str, remote_cmd: str, log_path: str) -> None:
"if command -v cloud-init >/dev/null 2>&1; then "
"cloud-init status 2>/dev/null || true; else echo nocloudinit; fi"
)
log_q = shlex.quote(log_path)
# On écrit un message d'attente + un battement toutes les ~30 s : sinon le
# log reste VIDE pendant tout le boot émulé et paraît « bloqué ».
msg_wait = t("Waiting for the VM to start (boot + cloud-init)")
msg_slow = t("(an emulated architecture can be slow; this is normal)")
msg_ready = t("VM ready - starting the ERPLibre install")
wrapper = (
f"for i in $(seq 1 144); do "
f"echo {shlex.quote('== ' + msg_wait + ' ==')} >> {log_q}; "
f"echo {shlex.quote(' ' + msg_slow)} >> {log_q}; "
f"for i in $(seq 1 240); do "
f"st=$(ssh {SSH_OPTS} -o BatchMode=yes erplibre@{ip} "
f"{shlex.quote(ci_probe)} 2>/dev/null); "
f'case "$st" in '
f"*done*|*disabled*|*error*|*degraded*|*nocloudinit*) break;; "
f"esac; sleep 5; done; "
f"esac; "
f'if [ $((i % 6)) -eq 0 ]; then echo " ... $((i*5))s" >> {log_q}; fi; '
f"sleep 5; done; "
f"echo {shlex.quote('== ' + msg_ready + ' ==')} >> {log_q}; "
f"ssh {SSH_OPTS} erplibre@{ip} {shlex.quote(remote_cmd)} "
f"> {shlex.quote(log_path)} 2>&1; "
f'echo "{EXIT_MARKER} $?" >> {shlex.quote(log_path)}'
f">> {log_q} 2>&1; "
f'echo "{EXIT_MARKER} $?" >> {log_q}'
)
# setsid -f : le process survit à la fermeture du menu / du dashboard.
subprocess.Popen(

View file

@ -1041,7 +1041,7 @@ class TODO:
distro = self._qemu_prompt_distro()
version = self._qemu_prompt_version(distro)
arch = self._qemu_prompt_arch(distro)
default_name = self._qemu_infra_name(distro, version)
default_name = self._qemu_infra_name(distro, version, arch)
name = (
input(f"{t('VM name (default: ')}{default_name}): ").strip()
or default_name
@ -1608,10 +1608,16 @@ class TODO:
spec.loader.exec_module(mod)
return mod
@staticmethod
def _qemu_infra_name(distro, version):
"""Nom de VM stable pour le parc, ex. erplibre-ubuntu-2404."""
return f"erplibre-{distro}-{version.replace('.', '')}"
@classmethod
def _qemu_infra_name(cls, distro, version, arch=None):
"""Nom de VM stable pour le parc, ex. erplibre-ubuntu-2404. Ajoute un
suffixe d'architecture quand elle diffère de la native de l'hôte (ex.
erplibre-ubuntu-2604-s390x sur un hôte amd64) pour éviter les collisions
de noms entre archis et rendre l'archi visible."""
base = f"erplibre-{distro}-{version.replace('.', '')}"
if arch and arch != cls._native_arch():
base += f"-{arch}"
return base
@staticmethod
def _parse_disk_gb(size):
@ -1743,7 +1749,7 @@ class TODO:
ERPLIBRE_EXTRA_DISK_GB = 5
@staticmethod
def _qemu_wait_ssh(ip, user="erplibre", timeout=720):
def _qemu_wait_ssh(ip, user="erplibre", timeout=1200):
"""Attend que sshd réponde ET que cloud-init soit TERMINÉ, via des
connexions COURTES successives. Au 1er boot, cloud-init régénère les
clés d'hôte et REDÉMARRE sshd : attendre la fin de cloud-init AVANT de
@ -2049,7 +2055,7 @@ class TODO:
free_ram = self._host_free_ram_mb()
print(f"\n{t('Deployment plan')} ({len(selected)} VM) :")
for d, v, ram, disk in selected:
name = self._qemu_infra_name(d, v)
name = self._qemu_infra_name(d, v, arch)
print(f" - {name:<26} {d} {v:<7} RAM {ram}Mo {t('disk')} {disk}")
print(f"\n {t('Total RAM (all running):')} {total_ram} Mo")
print(f" {t('Total virtual disk (thin qcow2):')} ~{total_disk} G")
@ -2108,7 +2114,7 @@ class TODO:
deployed = []
jobs = [] # (name, parts) des VM à créer
for d, v, _ram, disk in selected:
name = self._qemu_infra_name(d, v)
name = self._qemu_infra_name(d, v, arch)
if self._qemu_domain_exists(name):
print(f"{name}: {t('already exists, skipped.')}")
deployed.append(name)

View file

@ -1197,6 +1197,18 @@ TRANSLATIONS = {
"fr": "images pour cette architecture disponibles seulement pour :",
"en": "images for this arch only exist for:",
},
"Waiting for the VM to start (boot + cloud-init)": {
"fr": "En attente du démarrage de la VM (boot + cloud-init)",
"en": "Waiting for the VM to start (boot + cloud-init)",
},
"(an emulated architecture can be slow; this is normal)": {
"fr": "(une architecture émulée peut être lente ; c'est normal)",
"en": "(an emulated architecture can be slow; this is normal)",
},
"VM ready - starting the ERPLibre install": {
"fr": "VM prête — installation ERPLibre en cours",
"en": "VM ready - starting the ERPLibre install",
},
"Choice (number or name, blank = native):": {
"fr": "Choix (numéro ou nom, vide = native) :",
"en": "Choice (number or name, blank = native):",