[IMP] qemu: table lisible/scrollable, guest-exec autorisé, service Odoo, choix navigateur

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) <noreply@anthropic.com>
This commit is contained in:
Mathieu Benoit 2026-07-29 12:06:29 +00:00
parent ab8bfec113
commit 8278c38f46
3 changed files with 98 additions and 17 deletions

View file

@ -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"

View file

@ -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

View file

@ -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 <<UNIT\n"
"[Unit]\n"
"Description=ERPLibre\n"
"Requires=postgresql.service\n"
"After=network.target network-online.target postgresql.service\n"
"\n"
"[Service]\n"
"Type=simple\n"
"User=$SVC_USER\n"
"Group=$SVC_GROUP\n"
"Restart=always\n"
"RestartSec=5\n"
"ExecStart=$SVC_DIR/run.sh\n"
"WorkingDirectory=$SVC_DIR\n"
"StandardOutput=journal+console\n"
"\n"
"[Install]\n"
"WantedBy=multi-user.target\n"
"UNIT\n"
"sudo systemctl daemon-reload; "
"sudo systemctl enable --now erplibre.service"
)
def _qemu_erplibre_remote_cmd(self, branch, final_cmd=None):
"""Script d'installation ERPLibre exécuté DANS la VM (curl/git/make
puis clone + `final_cmd` dans ~/git/erplibre). `final_cmd` par défaut :
install_os + install_odoo_18."""
if not final_cmd:
final_cmd = f"make install_os && make {self.ERPLIBRE_ODOO_TARGET}"
# Profils AVEC Odoo (install_odoo*) uniquement : après l'install, on
# enregistre Odoo comme service systemd (enable + start). Pas pour
# « ERPLibre seul », « mobile » ni « Déploiement ».
if "install_odoo" in final_cmd:
final_cmd = f"{final_cmd} && {self._qemu_odoo_service_cmd()}"
return (
"set -e; "
# Attendre la FIN de cloud-init : pendant sa phase « paquets » il