[IMP] script todo: nb de VM dans le prompt parallèle + IDs de suivi (k/N)
Sur un gros parc (ex. 26 VM), on ne voyait ni combien de VM allaient être
déployées ni lesquelles étaient en cours (résultats parallèles dans le
désordre). Deux améliorations :
- Le prompt de parallélisme affiche le NOMBRE de VM à déployer :
« Déploiements en parallèle (défaut : 24, 26 VM) : ». Pour cela, la
séparation à-créer / déjà-existantes est faite AVANT le prompt (on connaît
le vrai compte).
- Chaque VM reçoit un ID « k/N » suivant l'ordre de préparation, affiché
dans les résultats de déploiement (« ✅ [3/26] nom ») et dans la résolution
d'IP (« [5/30] nom : ip »). L'ID reste stable même si les résultats
reviennent dans le désordre -> on suit lesquelles sont traitées.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
372aedb0eb
commit
a74b0c0c4c
2 changed files with 43 additions and 21 deletions
|
|
@ -1642,13 +1642,15 @@ 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):
|
def _qemu_resolve_ips(self, names, labels=None):
|
||||||
"""Résout les IP de plusieurs VM EN PARALLÈLE (le boot émulé est lent),
|
"""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}.
|
en affichant la progression au fur et à mesure. Renvoie {nom: ip|None}.
|
||||||
|
`labels` : {nom: « k/N »} pour préfixer chaque ligne d'un ID de suivi.
|
||||||
Évite l'attente EN SÉRIE et SANS sortie qui donnait l'impression d'un
|
Évite l'attente EN SÉRIE et SANS sortie qui donnait l'impression d'un
|
||||||
blocage (dashboard qui « n'ouvre jamais »)."""
|
blocage (dashboard qui « n'ouvre jamais »)."""
|
||||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||||
|
|
||||||
|
labels = labels or {}
|
||||||
print(f"\n{t('Resolving VM IPs (parallel, emulated boot is slow)...')}")
|
print(f"\n{t('Resolving VM IPs (parallel, emulated boot is slow)...')}")
|
||||||
result = {}
|
result = {}
|
||||||
workers = min(len(names), (os.cpu_count() or 4)) or 1
|
workers = min(len(names), (os.cpu_count() or 4)) or 1
|
||||||
|
|
@ -1661,7 +1663,8 @@ class TODO:
|
||||||
except Exception:
|
except Exception:
|
||||||
ip = None
|
ip = None
|
||||||
result[n] = ip
|
result[n] = ip
|
||||||
print(f" {n}: {ip or t('no IP')}")
|
prefix = f"[{labels[n]}] " if n in labels else ""
|
||||||
|
print(f" {prefix}{n}: {ip or t('no IP')}")
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def _qemu_vm_arch(self, name):
|
def _qemu_vm_arch(self, name):
|
||||||
|
|
@ -2256,37 +2259,48 @@ class TODO:
|
||||||
input(t("Add each VM to ~/.ssh/config? (y/N): "))
|
input(t("Add each VM to ~/.ssh/config? (y/N): "))
|
||||||
)
|
)
|
||||||
|
|
||||||
# Nombre de déploiements en parallèle. Défaut = nombre de CPU de
|
# 5) Sépare les VM à CRÉER des déjà existantes AVANT de proposer le
|
||||||
# l'hôte (borné par le nombre de VM ; repli sur 4 si indéterminé).
|
# parallélisme : on connaît alors le vrai nombre à déployer (affiché
|
||||||
default_par = min(len(selected), os.cpu_count() or 4)
|
# dans le prompt) et on peut numéroter chaque tâche.
|
||||||
|
pending = [] # (name, d, v, ram, disk, a)
|
||||||
|
deployed = []
|
||||||
|
for i, (d, v, ram, disk, a) in enumerate(selected):
|
||||||
|
name = names[i]
|
||||||
|
if self._qemu_domain_exists(name):
|
||||||
|
print(f"⏭ {name}: {t('already exists, skipped.')}")
|
||||||
|
deployed.append(name)
|
||||||
|
else:
|
||||||
|
pending.append((name, d, v, ram, disk, a))
|
||||||
|
n_jobs = len(pending)
|
||||||
|
|
||||||
|
# Nombre de déploiements en parallèle. Défaut = nombre de CPU de l'hôte
|
||||||
|
# (borné par le nombre de VM). On affiche aussi le NB DE VM à déployer.
|
||||||
|
default_par = min(n_jobs, os.cpu_count() or 4) or 1
|
||||||
raw = input(
|
raw = input(
|
||||||
f"{t('Parallel deployments (default:')} {default_par}): "
|
f"{t('Parallel deployments (default:')} {default_par}, "
|
||||||
|
f"{n_jobs} {t('VMs')}): "
|
||||||
).strip()
|
).strip()
|
||||||
try:
|
try:
|
||||||
parallelism = max(1, int(raw)) if raw else default_par
|
parallelism = max(1, int(raw)) if raw else default_par
|
||||||
except ValueError:
|
except ValueError:
|
||||||
parallelism = default_par
|
parallelism = default_par
|
||||||
|
|
||||||
# 5) Confirmation puis déploiement (en parallèle).
|
# Confirmation puis déploiement (en parallèle).
|
||||||
ans = input(f"\n{t('Deploy these VMs now? (y/N): ')}")
|
ans = input(f"\n{t('Deploy these VMs now? (y/N): ')}")
|
||||||
if not self._is_yes(ans):
|
if not self._is_yes(ans):
|
||||||
print(t("Cancelled."))
|
print(t("Cancelled."))
|
||||||
return
|
return
|
||||||
|
|
||||||
deployed = []
|
# Jobs numérotés (k/N) : l'ID suit l'ORDRE de préparation, stable même
|
||||||
jobs = [] # (name, parts) des VM à créer
|
# si les résultats reviennent dans le désordre (exécution parallèle).
|
||||||
for i, (d, v, ram, disk, a) in enumerate(selected):
|
jobs = [] # (id, name, parts)
|
||||||
name = names[i]
|
for k, (name, d, v, ram, disk, a) in enumerate(pending, 1):
|
||||||
if self._qemu_domain_exists(name):
|
|
||||||
print(f"⏭ {name}: {t('already exists, skipped.')}")
|
|
||||||
deployed.append(name)
|
|
||||||
continue
|
|
||||||
eram, evcpus = eff_res(ram)
|
eram, evcpus = eff_res(ram)
|
||||||
parts = self._qemu_build_deploy_parts(
|
parts = self._qemu_build_deploy_parts(
|
||||||
d, v, a, name, eram, evcpus, disk,
|
d, v, a, name, eram, evcpus, disk,
|
||||||
ssh_key, install_branch, dry_run=False,
|
ssh_key, install_branch, dry_run=False,
|
||||||
)
|
)
|
||||||
jobs.append((name, parts))
|
jobs.append((f"{k}/{n_jobs}", name, parts))
|
||||||
|
|
||||||
if jobs:
|
if jobs:
|
||||||
from concurrent.futures import (
|
from concurrent.futures import (
|
||||||
|
|
@ -2301,17 +2315,17 @@ class TODO:
|
||||||
)
|
)
|
||||||
|
|
||||||
def _run(job):
|
def _run(job):
|
||||||
jname, jparts = job
|
jid, jname, jparts = job
|
||||||
res = subprocess.run(jparts, capture_output=True, text=True)
|
res = subprocess.run(jparts, capture_output=True, text=True)
|
||||||
out = (res.stdout or "") + (res.stderr or "")
|
out = (res.stdout or "") + (res.stderr or "")
|
||||||
return jname, res.returncode, out
|
return jid, jname, res.returncode, out
|
||||||
|
|
||||||
with ThreadPoolExecutor(max_workers=workers) as pool:
|
with ThreadPoolExecutor(max_workers=workers) as pool:
|
||||||
futures = [pool.submit(_run, j) for j in jobs]
|
futures = [pool.submit(_run, j) for j in jobs]
|
||||||
for fut in as_completed(futures):
|
for fut in as_completed(futures):
|
||||||
jname, rc, out = fut.result()
|
jid, jname, rc, out = fut.result()
|
||||||
mark = "✅" if rc == 0 else "❌"
|
mark = "✅" if rc == 0 else "❌"
|
||||||
print(f"\n{mark} {jname} (rc={rc})")
|
print(f"\n{mark} [{jid}] {jname} (rc={rc})")
|
||||||
tail = [ln for ln in out.strip().splitlines() if ln][-4:]
|
tail = [ln for ln in out.strip().splitlines() if ln][-4:]
|
||||||
for ln in tail:
|
for ln in tail:
|
||||||
print(f" {ln}")
|
print(f" {ln}")
|
||||||
|
|
@ -2323,7 +2337,11 @@ class TODO:
|
||||||
# émulée SANS sortie -> le dashboard « n'ouvrait jamais ».
|
# émulée SANS sortie -> le dashboard « n'ouvrait jamais ».
|
||||||
ip_map = {}
|
ip_map = {}
|
||||||
if deployed and (add_ssh_config or install_branch):
|
if deployed and (add_ssh_config or install_branch):
|
||||||
ip_map = self._qemu_resolve_ips(deployed)
|
labels = {
|
||||||
|
nm: f"{k}/{len(deployed)}"
|
||||||
|
for k, nm in enumerate(deployed, 1)
|
||||||
|
}
|
||||||
|
ip_map = self._qemu_resolve_ips(deployed, labels)
|
||||||
|
|
||||||
if add_ssh_config:
|
if add_ssh_config:
|
||||||
for name in deployed:
|
for name in deployed:
|
||||||
|
|
|
||||||
|
|
@ -1633,6 +1633,10 @@ TRANSLATIONS = {
|
||||||
"fr": "Déploiements en parallèle (défaut :",
|
"fr": "Déploiements en parallèle (défaut :",
|
||||||
"en": "Parallel deployments (default:",
|
"en": "Parallel deployments (default:",
|
||||||
},
|
},
|
||||||
|
"VMs": {
|
||||||
|
"fr": "VM",
|
||||||
|
"en": "VMs",
|
||||||
|
},
|
||||||
"Deploying": {
|
"Deploying": {
|
||||||
"fr": "Déploiement de",
|
"fr": "Déploiement de",
|
||||||
"en": "Deploying",
|
"en": "Deploying",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue