[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:
parent
88dc1d5409
commit
0930f30cd3
3 changed files with 51 additions and 13 deletions
|
|
@ -20,6 +20,14 @@ import subprocess
|
||||||
import time
|
import time
|
||||||
from pathlib import Path
|
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__"
|
EXIT_MARKER = "__ERPLIBRE_EXIT__"
|
||||||
SSH_OPTS = (
|
SSH_OPTS = (
|
||||||
"-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "
|
"-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
|
"""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."""
|
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
|
# 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
|
# 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
|
# session SSH longue (ex. « cloud-init status --wait ») serait alors tuée
|
||||||
# (« Connection closed by remote host », exit 255 — cas Fedora). Chaque
|
# (« 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 "
|
"if command -v cloud-init >/dev/null 2>&1; then "
|
||||||
"cloud-init status 2>/dev/null || true; else echo nocloudinit; fi"
|
"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 = (
|
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"st=$(ssh {SSH_OPTS} -o BatchMode=yes erplibre@{ip} "
|
||||||
f"{shlex.quote(ci_probe)} 2>/dev/null); "
|
f"{shlex.quote(ci_probe)} 2>/dev/null); "
|
||||||
f'case "$st" in '
|
f'case "$st" in '
|
||||||
f"*done*|*disabled*|*error*|*degraded*|*nocloudinit*) break;; "
|
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"ssh {SSH_OPTS} erplibre@{ip} {shlex.quote(remote_cmd)} "
|
||||||
f"> {shlex.quote(log_path)} 2>&1; "
|
f">> {log_q} 2>&1; "
|
||||||
f'echo "{EXIT_MARKER} $?" >> {shlex.quote(log_path)}'
|
f'echo "{EXIT_MARKER} $?" >> {log_q}'
|
||||||
)
|
)
|
||||||
# setsid -f : le process survit à la fermeture du menu / du dashboard.
|
# setsid -f : le process survit à la fermeture du menu / du dashboard.
|
||||||
subprocess.Popen(
|
subprocess.Popen(
|
||||||
|
|
|
||||||
|
|
@ -1041,7 +1041,7 @@ class TODO:
|
||||||
distro = self._qemu_prompt_distro()
|
distro = self._qemu_prompt_distro()
|
||||||
version = self._qemu_prompt_version(distro)
|
version = self._qemu_prompt_version(distro)
|
||||||
arch = self._qemu_prompt_arch(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 = (
|
name = (
|
||||||
input(f"{t('VM name (default: ')}{default_name}): ").strip()
|
input(f"{t('VM name (default: ')}{default_name}): ").strip()
|
||||||
or default_name
|
or default_name
|
||||||
|
|
@ -1608,10 +1608,16 @@ class TODO:
|
||||||
spec.loader.exec_module(mod)
|
spec.loader.exec_module(mod)
|
||||||
return mod
|
return mod
|
||||||
|
|
||||||
@staticmethod
|
@classmethod
|
||||||
def _qemu_infra_name(distro, version):
|
def _qemu_infra_name(cls, distro, version, arch=None):
|
||||||
"""Nom de VM stable pour le parc, ex. erplibre-ubuntu-2404."""
|
"""Nom de VM stable pour le parc, ex. erplibre-ubuntu-2404. Ajoute un
|
||||||
return f"erplibre-{distro}-{version.replace('.', '')}"
|
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
|
@staticmethod
|
||||||
def _parse_disk_gb(size):
|
def _parse_disk_gb(size):
|
||||||
|
|
@ -1743,7 +1749,7 @@ class TODO:
|
||||||
ERPLIBRE_EXTRA_DISK_GB = 5
|
ERPLIBRE_EXTRA_DISK_GB = 5
|
||||||
|
|
||||||
@staticmethod
|
@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
|
"""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
|
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
|
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()
|
free_ram = self._host_free_ram_mb()
|
||||||
print(f"\n{t('Deployment plan')} ({len(selected)} VM) :")
|
print(f"\n{t('Deployment plan')} ({len(selected)} VM) :")
|
||||||
for d, v, ram, disk in selected:
|
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" - {name:<26} {d} {v:<7} RAM {ram}Mo {t('disk')} {disk}")
|
||||||
print(f"\n {t('Total RAM (all running):')} {total_ram} Mo")
|
print(f"\n {t('Total RAM (all running):')} {total_ram} Mo")
|
||||||
print(f" {t('Total virtual disk (thin qcow2):')} ~{total_disk} G")
|
print(f" {t('Total virtual disk (thin qcow2):')} ~{total_disk} G")
|
||||||
|
|
@ -2108,7 +2114,7 @@ class TODO:
|
||||||
deployed = []
|
deployed = []
|
||||||
jobs = [] # (name, parts) des VM à créer
|
jobs = [] # (name, parts) des VM à créer
|
||||||
for d, v, _ram, disk in selected:
|
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):
|
if self._qemu_domain_exists(name):
|
||||||
print(f"⏭ {name}: {t('already exists, skipped.')}")
|
print(f"⏭ {name}: {t('already exists, skipped.')}")
|
||||||
deployed.append(name)
|
deployed.append(name)
|
||||||
|
|
|
||||||
|
|
@ -1197,6 +1197,18 @@ TRANSLATIONS = {
|
||||||
"fr": "images pour cette architecture disponibles seulement pour :",
|
"fr": "images pour cette architecture disponibles seulement pour :",
|
||||||
"en": "images for this arch only exist for:",
|
"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):": {
|
"Choice (number or name, blank = native):": {
|
||||||
"fr": "Choix (numéro ou nom, vide = native) :",
|
"fr": "Choix (numéro ou nom, vide = native) :",
|
||||||
"en": "Choice (number or name, blank = native):",
|
"en": "Choice (number or name, blank = native):",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue