[FIX] script qemu: use /releases/ Ubuntu URLs, drop EOL versions, quiet crypt
Interim Ubuntu releases (24.10, 25.04) were removed from cloud-images /current/ once EOL, so their image URL 404'd. Switch the Ubuntu image URL to /releases/<version>/release/ubuntu-<version>-server-cloudimg- <arch>.img, which stays available for every published release, and drop the EOL interim versions from the catalogue (keep supported LTS + 25.10 + 26.04). Also: - silence the "crypt is deprecated" DeprecationWarning (openssl fallback already covers Python 3.13+); - a 404 during download now reports "image not found (EOL/removed), pick a supported LTS" instead of blaming connectivity; - add a NoCloud network-config (DHCP on e*) to the seed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
19e3801d2c
commit
990c81c0e7
2 changed files with 62 additions and 11 deletions
|
|
@ -56,7 +56,9 @@ import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
import textwrap
|
import textwrap
|
||||||
import time
|
import time
|
||||||
|
import urllib.error
|
||||||
import urllib.request
|
import urllib.request
|
||||||
|
import warnings
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
# --------------------------------------------------------------------------- #
|
# --------------------------------------------------------------------------- #
|
||||||
|
|
@ -67,12 +69,13 @@ from pathlib import Path
|
||||||
# DÉFAUT — la VM démarre au plus juste sans gaspiller la RAM de l'hôte.
|
# DÉFAUT — la VM démarre au plus juste sans gaspiller la RAM de l'hôte.
|
||||||
# --codename / --osinfo / --memory / --disk-size surchargent au besoin.
|
# --codename / --osinfo / --memory / --disk-size surchargent au besoin.
|
||||||
# --------------------------------------------------------------------------- #
|
# --------------------------------------------------------------------------- #
|
||||||
|
# NB : les versions intermédiaires (non-LTS) sont RETIRÉES du miroir
|
||||||
|
# cloud-images une fois EOL (leur /current/ renvoie 404). On ne garde donc
|
||||||
|
# que les LTS + les intermédiaires encore publiées. À réviser au fil du temps.
|
||||||
UBUNTU_VERSIONS: dict[str, tuple[str, str, int, str]] = {
|
UBUNTU_VERSIONS: dict[str, tuple[str, str, int, str]] = {
|
||||||
"20.04": ("focal", "ubuntu20.04", 2048, "5G"),
|
"20.04": ("focal", "ubuntu20.04", 2048, "5G"),
|
||||||
"22.04": ("jammy", "ubuntu22.04", 2048, "10G"),
|
"22.04": ("jammy", "ubuntu22.04", 2048, "10G"),
|
||||||
"24.04": ("noble", "ubuntu24.04", 3072, "20G"),
|
"24.04": ("noble", "ubuntu24.04", 3072, "20G"),
|
||||||
"24.10": ("oracular", "ubuntu24.10", 3072, "20G"),
|
|
||||||
"25.04": ("plucky", "ubuntu25.04", 3072, "20G"),
|
|
||||||
"25.10": ("questing", "ubuntu25.10", 3072, "20G"),
|
"25.10": ("questing", "ubuntu25.10", 3072, "20G"),
|
||||||
"26.04": ("resolute", "ubuntu26.04", 3072, "20G"),
|
"26.04": ("resolute", "ubuntu26.04", 3072, "20G"),
|
||||||
}
|
}
|
||||||
|
|
@ -142,9 +145,12 @@ def image_candidates(
|
||||||
pour Debian ; une seule URL pour Ubuntu/Fedora."""
|
pour Debian ; une seule URL pour Ubuntu/Fedora."""
|
||||||
a = distro_arch(distro, arch)
|
a = distro_arch(distro, arch)
|
||||||
if distro == "ubuntu":
|
if distro == "ubuntu":
|
||||||
|
# /releases/<version>/release/ : présent pour toutes les versions
|
||||||
|
# publiées (redirige vers l'image datée), contrairement à /current/
|
||||||
|
# qui disparaît quand une version intermédiaire devient EOL.
|
||||||
return [
|
return [
|
||||||
f"{CLOUD_IMG_BASE}/{code}/current/"
|
f"{CLOUD_IMG_BASE}/releases/{version}/release/"
|
||||||
f"{code}-server-cloudimg-{a}.img"
|
f"ubuntu-{version}-server-cloudimg-{a}.img"
|
||||||
]
|
]
|
||||||
if distro == "debian":
|
if distro == "debian":
|
||||||
return [
|
return [
|
||||||
|
|
@ -184,7 +190,7 @@ def default_image_name(distro: str, code: str, arch: str, version: str) -> str:
|
||||||
"""Nom de fichier local pour le cache d'image."""
|
"""Nom de fichier local pour le cache d'image."""
|
||||||
a = distro_arch(distro, arch)
|
a = distro_arch(distro, arch)
|
||||||
if distro == "ubuntu":
|
if distro == "ubuntu":
|
||||||
return f"{code}-server-cloudimg-{a}.img"
|
return f"ubuntu-{version}-server-cloudimg-{a}.img"
|
||||||
if distro == "debian":
|
if distro == "debian":
|
||||||
return f"debian-{version}-genericcloud-{a}.qcow2"
|
return f"debian-{version}-genericcloud-{a}.qcow2"
|
||||||
return f"fedora-cloud-{version}-{a}.qcow2"
|
return f"fedora-cloud-{version}-{a}.qcow2"
|
||||||
|
|
@ -628,6 +634,7 @@ def download_image(
|
||||||
)
|
)
|
||||||
tmp = dest.with_suffix(dest.suffix + ".part")
|
tmp = dest.with_suffix(dest.suffix + ".part")
|
||||||
errors = []
|
errors = []
|
||||||
|
had_404 = False
|
||||||
for i, url in enumerate(urls, 1):
|
for i, url in enumerate(urls, 1):
|
||||||
tag = "" if len(urls) == 1 else f" (miroir {i}/{len(urls)})"
|
tag = "" if len(urls) == 1 else f" (miroir {i}/{len(urls)})"
|
||||||
print(f" Téléchargement{tag} : {url}", flush=True)
|
print(f" Téléchargement{tag} : {url}", flush=True)
|
||||||
|
|
@ -635,15 +642,27 @@ def download_image(
|
||||||
_download_one(url, tmp, timeout)
|
_download_one(url, tmp, timeout)
|
||||||
tmp.replace(dest)
|
tmp.replace(dest)
|
||||||
return
|
return
|
||||||
except Exception as exc: # réseau/timeout : on tente le miroir suivant
|
except urllib.error.HTTPError as exc: # image absente sur ce miroir
|
||||||
|
tmp.unlink(missing_ok=True)
|
||||||
|
had_404 = had_404 or exc.code == 404
|
||||||
|
print(f"\n Échec : HTTP {exc.code}", flush=True)
|
||||||
|
errors.append(f"{url} -> HTTP {exc.code}")
|
||||||
|
except Exception as exc: # réseau/timeout : miroir suivant
|
||||||
tmp.unlink(missing_ok=True)
|
tmp.unlink(missing_ok=True)
|
||||||
print(f"\n Échec : {exc}", flush=True)
|
print(f"\n Échec : {exc}", flush=True)
|
||||||
errors.append(f"{url} -> {exc}")
|
errors.append(f"{url} -> {exc}")
|
||||||
|
hint = (
|
||||||
|
"\n Image introuvable (404) : cette version est probablement EOL et"
|
||||||
|
" a été retirée du miroir. Choisissez une version LTS encore"
|
||||||
|
" supportée (voir --list-images)."
|
||||||
|
if had_404
|
||||||
|
else "\n Vérifiez la connectivité (IPv6 ?), réessayez plus tard, ou"
|
||||||
|
" fournissez un chemin d'image local en argument positionnel."
|
||||||
|
)
|
||||||
sys.exit(
|
sys.exit(
|
||||||
"\nÉchec du téléchargement depuis tous les miroirs :\n "
|
"\nÉchec du téléchargement depuis tous les miroirs :\n "
|
||||||
+ "\n ".join(errors)
|
+ "\n ".join(errors)
|
||||||
+ "\n Vérifiez la connectivité (IPv6 ?), réessayez plus tard, ou "
|
+ hint
|
||||||
"fournissez un chemin d'image local en argument positionnel."
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -692,7 +711,11 @@ def verify_sha256(url: str, image: Path, dry_run: bool) -> None:
|
||||||
def hash_password(plain: str) -> str:
|
def hash_password(plain: str) -> str:
|
||||||
"""Hash SHA-512 crypt ($6$...) via crypt (< 3.13) ou openssl en repli."""
|
"""Hash SHA-512 crypt ($6$...) via crypt (< 3.13) ou openssl en repli."""
|
||||||
try:
|
try:
|
||||||
import crypt # supprimé en Python 3.13
|
with warnings.catch_warnings():
|
||||||
|
# crypt est déprécié (retiré en 3.13) : on masque l'avertissement,
|
||||||
|
# le repli openssl couvre les versions récentes de Python.
|
||||||
|
warnings.simplefilter("ignore", DeprecationWarning)
|
||||||
|
import crypt
|
||||||
|
|
||||||
return crypt.crypt(plain, crypt.mksalt(crypt.METHOD_SHA512))
|
return crypt.crypt(plain, crypt.mksalt(crypt.METHOD_SHA512))
|
||||||
except (ImportError, AttributeError):
|
except (ImportError, AttributeError):
|
||||||
|
|
@ -761,6 +784,20 @@ def build_cloud_config(
|
||||||
return "\n".join(lines) + "\n"
|
return "\n".join(lines) + "\n"
|
||||||
|
|
||||||
|
|
||||||
|
# network-config (cloud-init v2) : DHCP sur toute interface « e* ». Les images
|
||||||
|
# Debian genericcloud ne configurent pas toujours le réseau sans ça (le NIC
|
||||||
|
# reste down -> pas d'IP), contrairement à Ubuntu. Inoffensif pour Ubuntu.
|
||||||
|
NETWORK_CONFIG = (
|
||||||
|
"version: 2\n"
|
||||||
|
"ethernets:\n"
|
||||||
|
" primary:\n"
|
||||||
|
" match:\n"
|
||||||
|
' name: "e*"\n'
|
||||||
|
" dhcp4: true\n"
|
||||||
|
" dhcp6: false\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def build_seed(
|
def build_seed(
|
||||||
cloud_cfg: str, hostname: str, seed_dest: Path, runner: Runner
|
cloud_cfg: str, hostname: str, seed_dest: Path, runner: Runner
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|
@ -771,17 +808,30 @@ def build_seed(
|
||||||
tmp_path = Path(tmp)
|
tmp_path = Path(tmp)
|
||||||
ud = tmp_path / "user-data"
|
ud = tmp_path / "user-data"
|
||||||
md = tmp_path / "meta-data"
|
md = tmp_path / "meta-data"
|
||||||
|
nc = tmp_path / "network-config"
|
||||||
local_iso = tmp_path / "seed.iso"
|
local_iso = tmp_path / "seed.iso"
|
||||||
|
|
||||||
if runner.dry_run:
|
if runner.dry_run:
|
||||||
print(" [dry-run] user-data qui serait généré :")
|
print(" [dry-run] user-data qui serait généré :")
|
||||||
print(textwrap.indent(cloud_cfg, " "))
|
print(textwrap.indent(cloud_cfg, " "))
|
||||||
|
print(" [dry-run] network-config :")
|
||||||
|
print(textwrap.indent(NETWORK_CONFIG, " "))
|
||||||
else:
|
else:
|
||||||
ud.write_text(cloud_cfg)
|
ud.write_text(cloud_cfg)
|
||||||
md.write_text(meta_data)
|
md.write_text(meta_data)
|
||||||
|
nc.write_text(NETWORK_CONFIG)
|
||||||
|
|
||||||
if runner.dry_run or shutil.which("cloud-localds"):
|
if runner.dry_run or shutil.which("cloud-localds"):
|
||||||
runner.run(["cloud-localds", str(local_iso), str(ud), str(md)])
|
runner.run(
|
||||||
|
[
|
||||||
|
"cloud-localds",
|
||||||
|
"--network-config",
|
||||||
|
str(nc),
|
||||||
|
str(local_iso),
|
||||||
|
str(ud),
|
||||||
|
str(md),
|
||||||
|
]
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
need_tool("genisoimage")
|
need_tool("genisoimage")
|
||||||
runner.run(
|
runner.run(
|
||||||
|
|
@ -795,6 +845,7 @@ def build_seed(
|
||||||
"-rock",
|
"-rock",
|
||||||
str(ud),
|
str(ud),
|
||||||
str(md),
|
str(md),
|
||||||
|
str(nc),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -805,7 +805,7 @@ class TODO:
|
||||||
# deploy_qemu.py ; ceci ne sert qu'au sélecteur interactif.
|
# deploy_qemu.py ; ceci ne sert qu'au sélecteur interactif.
|
||||||
_QEMU_DISTROS = {
|
_QEMU_DISTROS = {
|
||||||
"ubuntu": (
|
"ubuntu": (
|
||||||
["20.04", "22.04", "24.04", "24.10", "25.04", "25.10", "26.04"],
|
["20.04", "22.04", "24.04", "25.10", "26.04"],
|
||||||
"24.04",
|
"24.04",
|
||||||
),
|
),
|
||||||
"debian": (["11", "12", "13"], "12"),
|
"debian": (["11", "12", "13"], "12"),
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue