From 814251f19fb60f06537c220ac29043e4105bce88 Mon Sep 17 00:00:00 2001 From: Mathieu Benoit Date: Mon, 14 Sep 2026 14:41:19 -0400 Subject: [PATCH] =?UTF-8?q?[FIX]=20d=C3=A9ploiement=20qemu=20:=20amorcer?= =?UTF-8?q?=20Fedora=20en=20BIOS,=20sa=20cha=C3=AEne=20UEFI=20se=20fige?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Aucune VM Fedora ne démarrait : pas de console, pas de bail DHCP, une machine « en cours d'exécution » qui ne fait rien. Le micrologiciel charge et DÉMARRE le chargeur — il l'annonce — puis se fige sans écrire un octet sur le disque. La même image en BIOS démarre son noyau : ce n'est donc ni l'image, saine, ni sa partition EFI, dont le chemin de repli est bien là. Ni l'entropie ni la machine q35 n'y changent rien. Une table nomme les distributions concernées et « --bios » l'emporte toujours, pour l'hôte qui n'a pas OVMF. --- EN --- No Fedora VM would start: no console, no DHCP lease, a machine "running" that does nothing. The firmware loads and STARTS the loader — it says so — then freezes without writing a byte to disk. The same image under BIOS boots its kernel: so it is neither the image, which is sound, nor its EFI partition, whose fallback path is present. Neither entropy nor the q35 machine changes anything. A table names the distributions concerned, and "--bios" always wins, for the host that has no OVMF. Assisted-by: Claude Opus 5 --- script/qemu/deploy_qemu.py | 30 ++++++++++++++++++++++++----- test/test_qemu_deploy_hote.py | 36 +++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 5 deletions(-) diff --git a/script/qemu/deploy_qemu.py b/script/qemu/deploy_qemu.py index e6ec03d..01a1447 100755 --- a/script/qemu/deploy_qemu.py +++ b/script/qemu/deploy_qemu.py @@ -1776,6 +1776,25 @@ def hostname_valide(nom: str) -> str: TZ_ALIASES = "/usr/share/zoneinfo/tzdata.zi" +# Distributions dont la chaîne UEFI ne démarre pas sur les OVMF courants. +# +# L'image de Fedora charge et DÉMARRE son chargeur — le micrologiciel l'annonce +# — puis se fige sans écrire un octet sur le disque. La même image en BIOS +# démarre son noyau normalement : ce n'est donc ni l'image, ni la partition +# EFI, dont le chemin de repli est bien là. Ni l'entropie ni la machine q35 n'y +# changent rien. +# +# Le symptôme visible depuis le déploiement est muet : aucune console, aucun +# bail DHCP, une VM « en cours d'exécution » qui ne fait rien. D'où cette table +# plutôt qu'un diagnostic à refaire. +BIOS_OBLIGATOIRE = {"fedora"} + + +def amorcage_bios(distro: str, demande: bool) -> bool: + """Faut-il amorcer en BIOS ? La demande explicite l'emporte toujours.""" + return bool(demande) or distro in BIOS_OBLIGATOIRE + + def canonical_timezone(tz: str, table: str = TZ_ALIASES) -> str: """Le nom canonique d'un fuseau, quand le système sait le dire. @@ -4018,11 +4037,10 @@ def virt_install( elif not args.bios: # Boot UEFI par défaut (x86) : Debian 13 (trixie) et les images cloud # récentes n'embarquent plus le chargeur BIOS/GRUB-pc et partent en - # boucle « Booting... » en SeaBIOS. UEFI (OVMF) fonctionne pour - # Ubuntu/Debian/Fedora. --bios force l'ancien BIOS si OVMF est absent. + # boucle « Booting... » en SeaBIOS. --bios force l'ancien BIOS, que + # certaines distributions exigent — voir BIOS_OBLIGATOIRE. # Secure Boot DÉSACTIVÉ : le chargeur d'Arch (GRUB) n'est pas signé et # OVMF Secure Boot le refuse (« Access Denied » -> pas de boot). - # Ubuntu/Debian/Fedora bootent aussi sans Secure Boot. cmd += [ "--boot", "uefi,firmware.feature0.name=secure-boot," @@ -4338,8 +4356,9 @@ def build_parser() -> argparse.ArgumentParser: g_vm.add_argument( "--bios", action="store_true", - help="Force l'amorçage BIOS hérité au lieu d'UEFI (par défaut UEFI ; " - "n'utiliser que si le firmware OVMF est absent).", + help="Force l'amorçage BIOS hérité au lieu d'UEFI. UEFI est le " + "défaut, sauf pour les distributions dont la chaîne UEFI ne démarre " + "pas (Fedora), où le BIOS est retenu d'office.", ) g_cloud = p.add_argument_group("cloud-init") @@ -4686,6 +4705,7 @@ def main() -> None: # rien d'autre qu'un avertissement de cloud-init ne le dise. Le nom de # DOMAINE, lui, peut le porter — les deux ne se ressemblent qu'en général. args.hostname = args.hostname or hostname_valide(args.name) + args.bios = amorcage_bios(args.distro, args.bios) pw_hash = resolve_password(args) ssh_keys = load_ssh_keys(args.ssh_key) diff --git a/test/test_qemu_deploy_hote.py b/test/test_qemu_deploy_hote.py index a42fa96..44ceb55 100644 --- a/test/test_qemu_deploy_hote.py +++ b/test/test_qemu_deploy_hote.py @@ -28,6 +28,7 @@ RACINE = Path(__file__).resolve().parent.parent sys.path.insert(0, str(RACINE)) from script.qemu.deploy_qemu import ( # noqa: E402 + amorcage_bios, canonical_timezone, hostname_valide, ) @@ -110,5 +111,40 @@ class TestFuseau(unittest.TestCase): ) +class TestAmorcage(unittest.TestCase): + """Quelle image démarre en UEFI, laquelle exige le BIOS. + + L'échec est MUET : le micrologiciel charge et démarre le chargeur, puis se + fige sans écrire un octet. Vu du déploiement, il ne reste qu'une VM « en + cours d'exécution » sans console et sans bail DHCP, et la cause est à + chercher pendant une heure. D'où une table plutôt qu'un diagnostic à + refaire. + """ + + def test_une_distribution_qui_lexige_part_en_bios(self): + self.assertTrue(amorcage_bios("fedora", False)) + + def test_les_autres_gardent_uefi(self): + """UEFI reste le défaut : les images cloud récentes n'embarquent plus + le chargeur BIOS et partent en boucle sous SeaBIOS.""" + for d in ( + "debian", + "ubuntu", + "arch", + "rocky", + "almalinux", + "opensuse", + ): + self.assertFalse(amorcage_bios(d, False), d) + + def test_la_demande_explicite_lemporte(self): + """« --bios » sert aussi quand OVMF manque de la machine hôte.""" + for d in ("debian", "fedora", "inconnue"): + self.assertTrue(amorcage_bios(d, True), d) + + def test_une_distribution_inconnue_garde_le_defaut(self): + self.assertFalse(amorcage_bios("inconnue", False)) + + if __name__ == "__main__": unittest.main()