[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: