[IMP] qemu: offer to install libvirt, and to create a missing SSH key

Two dead ends the menu let you walk into.

Without libvirt, every entry answered « sudo: virsh: command not found » and
carried on as if nothing had happened — « Aucune VM trouvée » reads like an
empty host, not a missing package. Entering the QEMU menu now checks for virsh
and offers to install it. The packages are not guessed here: deploy_qemu.py
--setup-host already knows them per distribution, so the offer just runs it.
Refusing keeps the menu open — listing images or previewing a deployment needs
no libvirt. If virsh is still absent afterwards, that is said too: on a
rolling-release kernel the setup can need a reboot before the modules load.

Without an SSH key, « Chemin de la clé publique SSH (aucune): » accepted an
empty answer and deployed VMs nobody could log into — cloud-init injects no
key, so there is no install, no check, nothing but a console. The prompt now
says what the consequence is and offers to generate one, reusing the same
_ssh_ensure_key as the SSH configuration tool so the whole fleet shares one
key.

Verified: silence when virsh is present, the sudo --setup-host command issued
when accepted and nothing run when refused; and on an empty ~/.ssh, the key
generated, both halves on disk, and its public path carried into the spec.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Mathieu Benoit 2026-08-02 05:56:06 -04:00
parent 3e53cbd47d
commit dcb1af4880
2 changed files with 68 additions and 1 deletions

View file

@ -1168,12 +1168,38 @@ class TODO:
print(f"{t('Will execute:')} {cmd}")
self.execute.exec_command_live(cmd, source_erplibre=False)
def _qemu_ensure_tools(self):
"""virsh absent : proposer l'installation plutôt que de laisser
chaque commande échouer sur « sudo: virsh: command not found ».
deploy_qemu.py --setup-host connaît les paquets de chaque
distribution ; on ne devine donc rien ici, on le délègue."""
if shutil.which("virsh"):
return True
print(f"\n{t('virsh is missing: libvirt is not installed here.')}")
print(f" {t('Every VM command will fail until it is.')}")
if not self._is_yes_default_yes(
input(t("Install the QEMU/libvirt tools now? (Y/n): "))
):
return False
cmd = f"sudo {self._QEMU_QEMU_PKGS}"
print(f"{t('Will execute:')} {cmd}")
self.execute.exec_command_live(cmd, source_erplibre=False)
if shutil.which("virsh"):
print(f"{t('libvirt is available.')}")
return True
# Sur une distribution à noyau roulant, --setup-host peut demander un
# redémarrage avant que les modules soient chargeables.
print(f"{t('virsh still missing; a reboot may be required.')}")
return False
def prompt_execute_qemu(self):
print(f"🤖 {t('Deploy a QEMU/KVM virtual machine (libvirt)!')}")
script_path = self._qemu_script_path()
if not os.path.isfile(script_path):
print(f"{t('QEMU deploy script not found: ')}{script_path}")
return False
self._qemu_ensure_tools()
choices = [
{"section": t("Deployment")},
{"prompt_description": t("Deploy VM(s) (one or many)")},
@ -5035,8 +5061,16 @@ class TODO:
"""Invites en ligne : clé SSH, installation ERPLibre, ~/.ssh/config,
parallélisme, puis récapitulatif et confirmation.
Renvoie la spec complète, ou None si l'utilisateur renonce."""
# Clé SSH (partagée par tout le parc).
# Clé SSH (partagée par tout le parc). Sans clé, cloud-init n'en
# injecte aucune : la VM démarre sans accès SSH, donc sans
# installation ni vérification possibles. On propose donc d'en créer
# une plutôt que de laisser passer un déploiement inutilisable.
default_key = self._qemu_default_ssh_key()
if not default_key:
print(f"\n{t('No SSH public key found in ~/.ssh.')}")
print(f" {t('Without one the VMs start with no SSH access.')}")
if self._is_yes_default_yes(input(t("Generate one now? (Y/n): "))):
default_key = self._ssh_ensure_key()
key_hint = default_key or t("none")
ssh_key = input(f"{t('SSH public key path')} ({key_hint}): ").strip()
if not ssh_key:

View file

@ -1419,6 +1419,39 @@ TRANSLATIONS = {
"fr": "Port local (défaut :",
"en": "Local port (default:",
},
"virsh is missing: libvirt is not installed here.": {
"fr": "virsh est absent : libvirt n'est pas installé ici.",
"en": "virsh is missing: libvirt is not installed here.",
},
"Every VM command will fail until it is.": {
"fr": "Toutes les commandes VM échoueront tant que ce sera le cas.",
"en": "Every VM command will fail until it is.",
},
"Install the QEMU/libvirt tools now? (Y/n): ": {
"fr": "Installer les outils QEMU/libvirt maintenant ? "
"(O/n, défaut : oui) : ",
"en": "Install the QEMU/libvirt tools now? (Y/n, default: yes): ",
},
"libvirt is available.": {
"fr": "libvirt est disponible.",
"en": "libvirt is available.",
},
"virsh still missing; a reboot may be required.": {
"fr": "virsh toujours absent ; un redémarrage est peut-être requis.",
"en": "virsh still missing; a reboot may be required.",
},
"No SSH public key found in ~/.ssh.": {
"fr": "Aucune clé publique SSH trouvée dans ~/.ssh.",
"en": "No SSH public key found in ~/.ssh.",
},
"Without one the VMs start with no SSH access.": {
"fr": "Sans elle, les VM démarrent sans accès SSH.",
"en": "Without one the VMs start with no SSH access.",
},
"Generate one now? (Y/n): ": {
"fr": "En générer une maintenant ? (O/n, défaut : oui) : ",
"en": "Generate one now? (Y/n, default: yes): ",
},
"Checking the remote port...": {
"fr": "Vérification du port distant...",
"en": "Checking the remote port...",