From 8278c38f4653cc0c68e4a6135d4d6940eacbb3a4 Mon Sep 17 00:00:00 2001 From: Mathieu Benoit Date: Wed, 29 Jul 2026 12:06:29 +0000 Subject: [PATCH] =?UTF-8?q?[IMP]=20qemu:=20table=20lisible/scrollable,=20g?= =?UTF-8?q?uest-exec=20autoris=C3=A9,=20service=20Odoo,=20choix=20navigate?= =?UTF-8?q?ur?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Suivi d'installation (qemu_install_monitor.py) : - Table VM/État LISIBLE : largeurs de colonnes fixes (VM 26, ⚠ 4, État 12, Durée 7, Disque 8) -> l'État n'est plus tronqué à 3-4 caractères (« ❌ effacée », « ⏸ en pause » lisibles) ; table défilable (overflow-x, height 1fr) pour les longs noms et les gros parcs. - « w » web : offre désormais la LISTE des navigateurs CLI installés (_choose_browser) pour choisir lequel utiliser, + option [i] installer. Déploiement (deploy_qemu.py) : - guest-exec AUTORISÉ : on vide la liste de blocage de qemu-ga (block-rpcs/blacklist vides — pas allow-rpcs qui est une liste BLANCHE et casserait les autres RPC), + neutralise /etc/sysconfig/qemu-ga (Fedora). Installation (todo.py) : - Profils AVEC Odoo (install_odoo*) UNIQUEMENT : Odoo est enregistré comme service systemd (erplibre.service, inspiré de script/systemd/ install_daemon.sh) puis enable --now. Pas pour ERPLibre seul / mobile / Déploiement. Co-Authored-By: Claude Opus 4.8 (1M context) --- script/qemu/deploy_qemu.py | 19 ++++++++- script/todo/qemu_install_monitor.py | 60 +++++++++++++++++++++-------- script/todo/todo.py | 36 +++++++++++++++++ 3 files changed, 98 insertions(+), 17 deletions(-) diff --git a/script/qemu/deploy_qemu.py b/script/qemu/deploy_qemu.py index ff05976..2cafd02 100755 --- a/script/qemu/deploy_qemu.py +++ b/script/qemu/deploy_qemu.py @@ -978,8 +978,23 @@ def build_cloud_config( " qemu-guest-agent) || (command -v dnf >/dev/null && dnf install -y" " qemu-guest-agent) || (command -v pacman >/dev/null && pacman -S" " --noconfirm qemu-guest-agent) || true", - " - systemctl enable --now qemu-guest-agent 2>/dev/null" - " || systemctl enable --now qemu-ga 2>/dev/null || true", + # Autorise guest-exec (bloqué par défaut sur certaines distros). + # On VIDE la liste de blocage (block-rpcs/blacklist) plutôt que + # d'utiliser allow-rpcs (liste BLANCHE : casserait les autres RPC). + # Les deux clés couvrent les versions anciennes (blacklist) et + # récentes (block-rpcs) de qemu-ga. + " - mkdir -p /etc/qemu && printf '[general]\\nblock-rpcs=\\n" + "blacklist=\\n' > /etc/qemu/qemu-ga.conf || true", + # Fedora/RHEL bloquent guest-exec via /etc/sysconfig/qemu-ga. + " - [ -f /etc/sysconfig/qemu-ga ] && sed -i" + " 's/^BLACKLIST_RPC=.*/BLACKLIST_RPC=/'" + " /etc/sysconfig/qemu-ga || true", + " - systemctl enable qemu-guest-agent 2>/dev/null" + " || systemctl enable qemu-ga 2>/dev/null || true", + # restart (et non enable --now) : recharge la config si l'agent était + # déjà démarré par l'image cloud. + " - systemctl restart qemu-guest-agent 2>/dev/null" + " || systemctl restart qemu-ga 2>/dev/null || true", ] return "\n".join(lines) + "\n" diff --git a/script/todo/qemu_install_monitor.py b/script/todo/qemu_install_monitor.py index 62c96de..65749e5 100644 --- a/script/todo/qemu_install_monitor.py +++ b/script/todo/qemu_install_monitor.py @@ -470,7 +470,7 @@ def run_monitor(manifest_path: str, run_app: bool = True): class Monitor(App): CSS = """ - DataTable { width: 58; border: solid $accent; } + DataTable { width: 74; height: 1fr; overflow-x: auto; border: solid $accent; } RichLog { border: solid $accent; } #telemetry { height: 1; color: $text-muted; } #stats { height: 1; color: $accent; } @@ -526,11 +526,14 @@ def run_monitor(manifest_path: str, run_app: bool = True): table = DataTable(id="vms", cursor_type="row") # Clés de colonnes explicites : update_cell() les référence. # La colonne « ⚠ » (erreurs détectées) est à GAUCHE d'« État ». - table.add_column("VM", key="vm") - table.add_column("⚠", key="err") - table.add_column("État", key="state") - table.add_column("Durée", key="elapsed") - table.add_column("Disque", key="disk") + # Largeurs FIXES pour les colonnes courtes -> l'État n'est plus + # tronqué (« ❌ effacée », « ⏸ en pause » lisibles) ; la table + # défile horizontalement (overflow-x) pour les noms de VM longs. + table.add_column("VM", key="vm", width=26) + table.add_column("⚠", key="err", width=4) + table.add_column("État", key="state", width=12) + table.add_column("Durée", key="elapsed", width=7) + table.add_column("Disque", key="disk", width=8) for vm in vms: table.add_row( vm["name"], "", "⏳", "--:--", "-", key=vm["name"] @@ -835,16 +838,9 @@ def run_monitor(manifest_path: str, run_app: bool = True): if not vm or not vm.get("ip"): self.notify("Pas d'IP pour cette VM.", title="Web") return - browser = cli_browser() + browser = self._choose_browser() if not browser: - browser = self._install_cli_browser() - if not browser: - self.notify( - "Aucun navigateur CLI disponible.", - title="Web", - severity="warning", - ) - return + return url = f"http://{vm['ip']}:8069" with self.suspend(): print(f"→ {browser} {url}") @@ -863,6 +859,40 @@ def run_monitor(manifest_path: str, run_app: bool = True): except EOFError: pass + def _choose_browser(self): + """Offre la LISTE des navigateurs CLI installés et laisse choisir + lequel utiliser pour voir la page. Si aucun n'est installé, propose + d'en installer un. Renvoie le binaire choisi, ou None.""" + available = [b for b in CLI_BROWSERS if shutil.which(b)] + if not available: + browser = self._install_cli_browser() + if not browser: + self.notify( + "Aucun navigateur CLI disponible.", + title="Web", + severity="warning", + ) + return browser + with self.suspend(): + print("Quel navigateur utiliser pour voir la page ?") + for i, b in enumerate(available, 1): + print(f" [{i}] {b}{' *' if i == 1 else ''}") + print(" [i] Installer un autre navigateur") + sel = input( + f"Choix (numéro, vide = {available[0]}) : " + ).strip().lower() + if sel == "i": + return self._install_cli_browser() + if not sel: + return available[0] + try: + idx = int(sel) - 1 + if 0 <= idx < len(available): + return available[idx] + except ValueError: + pass + return available[0] + def _install_cli_browser(self): """Demande QUEL navigateur CLI installer (w3m/lynx/links/elinks), affiche la commande, l'exécute après validation. Renvoie le binaire diff --git a/script/todo/todo.py b/script/todo/todo.py index fee956f..c2ff56a 100755 --- a/script/todo/todo.py +++ b/script/todo/todo.py @@ -2533,12 +2533,48 @@ class TODO: pass return profiles[0] # défaut : ERPLibre + Odoo 18 + @staticmethod + def _qemu_odoo_service_cmd(): + """Snippet shell (exécuté dans la VM) qui installe ERPLibre/Odoo comme + service systemd (inspiré de script/systemd/install_daemon.sh) puis + l'active + démarre. N'est ajouté QUE pour les profils avec Odoo.""" + return ( + 'SVC_USER=$(whoami); SVC_GROUP=$(id -gn); ' + 'SVC_DIR="$HOME/git/erplibre"; ' + "sudo tee /etc/systemd/system/erplibre.service >/dev/null <