Integrate claude, improve selenium and support qemu #1

Open
mathben wants to merge 224 commits from develop into master
Showing only changes of commit b49e107b83 - Show all commits

View file

@ -1763,6 +1763,28 @@ class TODO:
# Meilleur effort : le dernier bail (le plus récent) plutôt que le 1er. # Meilleur effort : le dernier bail (le plus récent) plutôt que le 1er.
return cands[-1] if cands else None return cands[-1] if cands else None
def _qemu_resolve_ips(self, names):
"""Résout les IP de plusieurs VM EN PARALLÈLE (le boot émulé est lent),
en affichant la progression au fur et à mesure. Renvoie {nom: ip|None}.
Évite l'attente EN SÉRIE et SANS sortie qui donnait l'impression d'un
blocage (dashboard qui « n'ouvre jamais »)."""
from concurrent.futures import ThreadPoolExecutor, as_completed
print(f"\n{t('Resolving VM IPs (parallel, emulated boot is slow)...')}")
result = {}
workers = min(len(names), (os.cpu_count() or 4)) or 1
with ThreadPoolExecutor(max_workers=workers) as pool:
futs = {pool.submit(self._qemu_vm_ip, n): n for n in names}
for fut in as_completed(futs):
n = futs[fut]
try:
ip = fut.result()
except Exception:
ip = None
result[n] = ip
print(f" {n}: {ip or t('no IP')}")
return result
def _qemu_vm_arch(self, name): def _qemu_vm_arch(self, name):
"""Architecture d'une VM (jeton amd64/arm64/s390x) via virsh dumpxml.""" """Architecture d'une VM (jeton amd64/arm64/s390x) via virsh dumpxml."""
try: try:
@ -1952,10 +1974,10 @@ class TODO:
f"make {self.ERPLIBRE_ODOO_TARGET}" f"make {self.ERPLIBRE_ODOO_TARGET}"
) )
def _qemu_install_erplibre_monitored(self, names, branch): def _qemu_install_erplibre_monitored(self, names, branch, ip_map=None):
"""Lance l'install ERPLibre en parallèle DÉTACHÉE sur les VM et ouvre """Lance l'install ERPLibre en parallèle DÉTACHÉE sur les VM et ouvre
le dashboard Textual. Quitter le dashboard n'arrête pas les installs. le dashboard Textual. Quitter le dashboard n'arrête pas les installs.
""" `ip_map` : IP déjà résolues (sinon on résout ici, EN PARALLÈLE)."""
from script.todo.qemu_install_monitor import ( from script.todo.qemu_install_monitor import (
launch_installs, launch_installs,
run_monitor, run_monitor,
@ -1966,10 +1988,11 @@ class TODO:
mod = self._qemu_import_module() mod = self._qemu_import_module()
except Exception: except Exception:
mod = None mod = None
if ip_map is None:
ip_map = self._qemu_resolve_ips(names)
vms = [] vms = []
for name in names: for name in names:
print(f" {name}: {t('resolving IP...')}") ip = ip_map.get(name)
ip = self._qemu_vm_ip(name)
if ip: if ip:
d, v, a = ( d, v, a = (
self._qemu_vm_meta(name, mod) self._qemu_vm_meta(name, mod)
@ -2012,9 +2035,11 @@ class TODO:
# Commande prête à copier pour relire/partager tous les logs. # Commande prête à copier pour relire/partager tous les logs.
print(f" {t('Read the logs:')} tail -n +1 {logdir}/*.log") print(f" {t('Read the logs:')} tail -n +1 {logdir}/*.log")
def _qemu_install_erplibre_vm(self, name, ssh_key, branch): def _qemu_install_erplibre_vm(self, name, ssh_key, branch, ip=None):
"""Clone ERPLibre (branche donnée) dans ~/git/erplibre de la VM puis """Clone ERPLibre (branche donnée) dans ~/git/erplibre de la VM puis
exécute « make install_os » et « make install_odoo_18 » (streamé).""" exécute « make install_os » et « make install_odoo_18 » (streamé).
`ip` : IP déjà résolue (sinon on la résout ici)."""
if ip is None:
ip = self._qemu_vm_ip(name) ip = self._qemu_vm_ip(name)
if not ip: if not ip:
print( print(
@ -2316,10 +2341,16 @@ class TODO:
if rc == 0: if rc == 0:
deployed.append(jname) deployed.append(jname)
# 6) ~/.ssh/config (une fois les VM démarrées et l'IP attribuée). # 6) Résolution des IP EN PARALLÈLE (réutilisée pour ssh_config +
# install) : une boucle EN SÉRIE bloquait plusieurs minutes par VM
# émulée SANS sortie -> le dashboard « n'ouvrait jamais ».
ip_map = {}
if deployed and (add_ssh_config or install_branch):
ip_map = self._qemu_resolve_ips(deployed)
if add_ssh_config: if add_ssh_config:
for name in deployed: for name in deployed:
ip = self._qemu_vm_ip(name) ip = ip_map.get(name)
if ip: if ip:
self._write_ssh_config_entry(name, "erplibre", ip) self._write_ssh_config_entry(name, "erplibre", ip)
@ -2327,7 +2358,9 @@ class TODO:
if install_branch: if install_branch:
if install_monitor: if install_monitor:
# Installs détachées en parallèle + dashboard Textual. # Installs détachées en parallèle + dashboard Textual.
self._qemu_install_erplibre_monitored(deployed, install_branch) self._qemu_install_erplibre_monitored(
deployed, install_branch, ip_map
)
else: else:
print( print(
f"\n{t('Installing ERPLibre on each VM')} " f"\n{t('Installing ERPLibre on each VM')} "
@ -2335,7 +2368,7 @@ class TODO:
) )
for name in deployed: for name in deployed:
self._qemu_install_erplibre_vm( self._qemu_install_erplibre_vm(
name, ssh_key, install_branch name, ssh_key, install_branch, ip_map.get(name)
) )
print(f"\n✅ {t('ERPLibre infra deployment done.')}") print(f"\n✅ {t('ERPLibre infra deployment done.')}")