[FIX] qemu: Fedora image robuste (miroirs) + silence du bruit virtinst

Déploiement PARALLÈLE de 13 VM -> 2 problèmes observés :

1. « Aucune image Fedora-Cloud-Base-Generic pour Fedora 42 » alors que
   l'image EXISTE. download.fedoraproject.org est un redirecteur
   (MirrorManager) : sous requêtes parallèles, une VM tombait sur un miroir
   incomplet -> index HTML sans correspondance -> sys.exit.
   resolve_fedora_url réessaie (2× le redirecteur) puis se replie sur le
   serveur MAÎTRE dl.fedoraproject.org (toujours complet). Validé : F41/F42
   résolus.

2. Pavé « Fetched capabilities … » (énorme XML) dans la sortie : erreur de
   LOGGING de virtinst — sous sudo, HOME/cache inaccessible -> échec
   d'écriture du journal debug -> Python déverse l'enregistrement raté.
   On préfixe virt-install de « env XDG_CACHE_HOME=… HOME=… » (traverse
   sudo) vers un cache écrivable -> journal silencieux.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Mathieu Benoit 2026-07-30 01:41:17 -04:00
parent b99db0ea66
commit 1ebf2b353f

View file

@ -161,6 +161,10 @@ DEBIAN_CLOUD_BASES: tuple[str, ...] = (
"https://laotzu.ftp.acc.umu.se/cdimage/cloud", "https://laotzu.ftp.acc.umu.se/cdimage/cloud",
) )
FEDORA_BASE = "https://download.fedoraproject.org/pub/fedora/linux/releases" FEDORA_BASE = "https://download.fedoraproject.org/pub/fedora/linux/releases"
# Serveur MAÎTRE (pas de redirection MirrorManager) : repli fiable quand le
# redirecteur envoie sur un miroir incomplet (fréquent en déploiement
# PARALLÈLE : chaque requête peut tomber sur un miroir différent/désynchronisé).
FEDORA_BASE_MASTER = "https://dl.fedoraproject.org/pub/fedora/linux/releases"
# Répertoire de cache par défaut des images cloud (cohérent avec --disk-dir / # Répertoire de cache par défaut des images cloud (cohérent avec --disk-dir /
# --seed-dir). L'écriture y nécessite root : le déploiement tourne de toute # --seed-dir). L'écriture y nécessite root : le déploiement tourne de toute
@ -214,26 +218,38 @@ def image_candidates(
def resolve_fedora_url(version: str, arch: str, dry_run: bool) -> str: def resolve_fedora_url(version: str, arch: str, dry_run: bool) -> str:
"""Résout l'URL du qcow2 « Fedora Cloud Base Generic » depuis l'index """Résout l'URL du qcow2 « Fedora Cloud Base Generic » depuis l'index
HTML des releases (Fedora ne publie pas de lien « latest »).""" HTML des releases (Fedora ne publie pas de lien « latest »). ROBUSTE :
plusieurs tentatives sur le redirecteur puis repli sur le serveur maître
(dl.fedoraproject.org) — sinon, en déploiement parallèle, une requête
tombée sur un miroir incomplet faisait échouer la VM (« image introuvable »)
alors que l'image existe bel et bien."""
a = distro_arch("fedora", arch) a = distro_arch("fedora", arch)
index = f"{FEDORA_BASE}/{version}/Cloud/{a}/images/"
pattern = re.compile( pattern = re.compile(
rf"Fedora-Cloud-Base-Generic-{version}-[0-9.]+\.{a}\.qcow2" rf"Fedora-Cloud-Base-Generic-{version}-[0-9.]+\.{a}\.qcow2"
) )
if dry_run: if dry_run:
index = f"{FEDORA_BASE}/{version}/Cloud/{a}/images/"
return index + f"Fedora-Cloud-Base-Generic-{version}-<build>.{a}.qcow2" return index + f"Fedora-Cloud-Base-Generic-{version}-<build>.{a}.qcow2"
try: # On essaie le redirecteur (2 fois : chaque requête peut viser un miroir
with urllib.request.urlopen(index, timeout=30) as resp: # noqa: S310 # différent) puis le serveur maître (toujours complet).
html = resp.read().decode(errors="replace") bases = [FEDORA_BASE, FEDORA_BASE, FEDORA_BASE_MASTER]
except Exception as exc: # pragma: no cover - dépend du réseau last_err = ""
sys.exit(f"Impossible de lister les images Fedora {version} : {exc}") for base in bases:
names = sorted(set(pattern.findall(html))) index = f"{base}/{version}/Cloud/{a}/images/"
if not names: try:
sys.exit( with urllib.request.urlopen(index, timeout=30) as resp: # noqa: S310
"Aucune image « Fedora-Cloud-Base-Generic » trouvée pour " html = resp.read().decode(errors="replace")
f"Fedora {version} ({a}) dans {index}" except Exception as exc: # pragma: no cover - dépend du réseau
) last_err = str(exc)
return index + names[-1] continue
names = sorted(set(pattern.findall(html)))
if names:
return index + names[-1]
sys.exit(
"Aucune image « Fedora-Cloud-Base-Generic » trouvée pour "
f"Fedora {version} ({a}) après plusieurs miroirs"
+ (f" (dernière erreur : {last_err})" if last_err else "")
)
def default_image_name(distro: str, code: str, arch: str, version: str) -> str: def default_image_name(distro: str, code: str, arch: str, version: str) -> str:
@ -1241,7 +1257,17 @@ def virt_install(
cmd += ["--virt-type", "qemu"] cmd += ["--virt-type", "qemu"]
if not args.attach_console: if not args.attach_console:
cmd.append("--noautoconsole") cmd.append("--noautoconsole")
runner.run(cmd, privileged=True) # virtinst écrit un journal de debug dans ~/.cache/virt-manager ; sous
# sudo, HOME/cache peut être inaccessible -> l'écriture échoue et Python
# déverse un « Logging error » (le pavé « Fetched capabilities … »). On
# force un cache/HOME ÉCRIVABLE via « env VAR=… » (traverse sudo) pour
# que le journal s'écrive silencieusement.
log_env = [
"env",
"XDG_CACHE_HOME=/var/tmp/erplibre-virtinst",
"HOME=/var/tmp/erplibre-virtinst",
]
runner.run(log_env + cmd, privileged=True)
def _ip_reachable(ip: str, port: int = 22, timeout: float = 3) -> bool: def _ip_reachable(ip: str, port: int = 22, timeout: float = 3) -> bool: