From dcb1af48802b58c7ba27ead14b4a5f6311d59923 Mon Sep 17 00:00:00 2001 From: Mathieu Benoit Date: Sun, 2 Aug 2026 05:56:06 -0400 Subject: [PATCH] [IMP] qemu: offer to install libvirt, and to create a missing SSH key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- script/todo/todo.py | 36 +++++++++++++++++++++++++++++++++++- script/todo/todo_i18n.py | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/script/todo/todo.py b/script/todo/todo.py index df67a63..cc01953 100755 --- a/script/todo/todo.py +++ b/script/todo/todo.py @@ -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: diff --git a/script/todo/todo_i18n.py b/script/todo/todo_i18n.py index 9942eb5..a85d71a 100644 --- a/script/todo/todo_i18n.py +++ b/script/todo/todo_i18n.py @@ -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...",