[ADD] proxmox : un écran pour déployer sur un hôte distant
Le déploiement Proxmox se faisait par vingt questions, l'une après
l'autre, sans jamais voir le plan entier. Il a maintenant le même écran
que QEMU/KVM — catalogue à gauche, plan à droite, totaux dessous — et une
touche pour revenir aux questions. Il n'en redit rien : 690 lignes contre
1 340, tout le reste vient du socle commun.
Ce que Proxmox a en propre est montré AVANT de lancer : le VMID, parce
que l'hôte ne dit « déjà pris » qu'après avoir téléchargé l'image, et
l'adresse qui s'en déduit sur un pont interne. Le stockage et le pont sont
lus sur l'hôte, jamais devinés. L'icône 🗄 distingue l'entrée du menu de
celle qui déploie ici même.
--- EN ---
Deploying on Proxmox meant twenty questions, one after another, never
seeing the whole plan. It now has the same screen as QEMU/KVM — catalogue
left, plan right, totals below — and a key to fall back to the questions.
It repeats none of it: 690 lines against 1,340, everything else comes
from the shared foundation.
What belongs to Proxmox is shown BEFORE launching: the VMID, because the
host only says "already taken" after downloading the image, and the
address derived from it on an internal bridge. Storage and bridge are read
from the host, never guessed. The 🗄 icon tells the menu entry apart from
the one that deploys right here.
Assisted-by: Claude Opus 5
2026-08-23 18:01:28 -04:00
|
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
# © 2021-2026 TechnoLibre (http://www.technolibre.ca)
|
|
|
|
|
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
|
|
|
|
|
|
"""Formulaire de déploiement sur un hôte Proxmox VE.
|
|
|
|
|
|
|
|
|
|
|
|
Même écran que pour QEMU/KVM — catalogue à gauche, plan à droite, totaux
|
|
|
|
|
|
dessous — parce que c'est le même travail : choisir des systèmes, régler des
|
|
|
|
|
|
ressources, vérifier avant de lancer. Tout ce qui est commun vient de
|
|
|
|
|
|
`deploy_form_lib` (logique pure, socle CSS, fabrique des ressources) et de
|
|
|
|
|
|
`deploy_form_plan` (surcharges, verrous, exemplaires, renommage). Ne reste
|
|
|
|
|
|
ici que ce que Proxmox a en propre :
|
|
|
|
|
|
|
|
|
|
|
|
* l'hôte, choisi AVANT d'ouvrir l'écran — il faut ssh et sudo, et une invite
|
|
|
|
|
|
de mot de passe pendant que Textual affiche casserait le terminal ;
|
|
|
|
|
|
* le stockage et le pont, LUS SUR L'HÔTE : « local-lvm » n'existe pas partout
|
|
|
|
|
|
et un pont inventé fait échouer « qm create » ;
|
|
|
|
|
|
* le VMID, et l'adresse qui s'en déduit sur un pont interne.
|
|
|
|
|
|
|
|
|
|
|
|
Le formulaire ne touche à rien : il rend une spec. C'est l'appelant
|
|
|
|
|
|
(`ProxmoxMenuMixin._pve_deploy`) qui exécute.
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
import os
|
[ADD] todo : dire la place qui reste sous le plan de déploiement
La ligne de totaux annonçait « ~126 G » sans dire sur quoi : la demande
seule ne dit pas si ça rentre, et on l'apprenait au déploiement. Elle dit
maintenant la demande, ce qui reste et la capacité — « ~126 G / 20 G libres
sur 270 G » — et prévient dès que le plan dépasse. Les trois limites (RAM,
disque, cœurs) s'affichent ensemble : n'en montrer qu'une cachait les
autres. Sur Proxmox, la place vient du stockage choisi, que « pvesm status »
donnait déjà.
Deux défauts trouvés en le faisant : la marque de génération, exigée de tous
les widgets, faisait taire chaque réglage commun de l'écran Proxmox, et
« [x1] » disparaissait, lu comme une balise Rich.
--- EN ---
The totals line said "~126 G" without saying out of what: the demand alone
does not tell whether it fits, and one found out at deploy time. It now says
the demand, what is left and the capacity — "~126 G / 20 G free of 270 G" —
and warns as soon as the plan exceeds it. The three limits (RAM, disk,
cores) show together: showing only one hid the others. On Proxmox the room
comes from the chosen storage, which "pvesm status" already gave.
Two defects found on the way: the generation mark, required of every widget,
silenced each common setting on the Proxmox screen, and "[x1]" vanished,
read as a Rich tag.
Assisted-by: Claude Opus 5
2026-08-24 01:11:09 -04:00
|
|
|
|
import re
|
[ADD] proxmox : un écran pour déployer sur un hôte distant
Le déploiement Proxmox se faisait par vingt questions, l'une après
l'autre, sans jamais voir le plan entier. Il a maintenant le même écran
que QEMU/KVM — catalogue à gauche, plan à droite, totaux dessous — et une
touche pour revenir aux questions. Il n'en redit rien : 690 lignes contre
1 340, tout le reste vient du socle commun.
Ce que Proxmox a en propre est montré AVANT de lancer : le VMID, parce
que l'hôte ne dit « déjà pris » qu'après avoir téléchargé l'image, et
l'adresse qui s'en déduit sur un pont interne. Le stockage et le pont sont
lus sur l'hôte, jamais devinés. L'icône 🗄 distingue l'entrée du menu de
celle qui déploie ici même.
--- EN ---
Deploying on Proxmox meant twenty questions, one after another, never
seeing the whole plan. It now has the same screen as QEMU/KVM — catalogue
left, plan right, totals below — and a key to fall back to the questions.
It repeats none of it: 690 lines against 1,340, everything else comes
from the shared foundation.
What belongs to Proxmox is shown BEFORE launching: the VMID, because the
host only says "already taken" after downloading the image, and the
address derived from it on an internal bridge. Storage and bridge are read
from the host, never guessed. The 🗄 icon tells the menu entry apart from
the one that deploys right here.
Assisted-by: Claude Opus 5
2026-08-23 18:01:28 -04:00
|
|
|
|
|
|
|
|
|
|
from script.todo.deploy_form_lib import (
|
|
|
|
|
|
CSS_BASE,
|
|
|
|
|
|
FREE,
|
|
|
|
|
|
RES_FIELDS,
|
|
|
|
|
|
SELECT_TO_FIELD,
|
|
|
|
|
|
build_vms,
|
[ADD] todo : dire la place qui reste sous le plan de déploiement
La ligne de totaux annonçait « ~126 G » sans dire sur quoi : la demande
seule ne dit pas si ça rentre, et on l'apprenait au déploiement. Elle dit
maintenant la demande, ce qui reste et la capacité — « ~126 G / 20 G libres
sur 270 G » — et prévient dès que le plan dépasse. Les trois limites (RAM,
disque, cœurs) s'affichent ensemble : n'en montrer qu'une cachait les
autres. Sur Proxmox, la place vient du stockage choisi, que « pvesm status »
donnait déjà.
Deux défauts trouvés en le faisant : la marque de génération, exigée de tous
les widgets, faisait taire chaque réglage commun de l'écran Proxmox, et
« [x1] » disparaissait, lu comme une balise Rich.
--- EN ---
The totals line said "~126 G" without saying out of what: the demand alone
does not tell whether it fits, and one found out at deploy time. It now says
the demand, what is left and the capacity — "~126 G / 20 G free of 270 G" —
and warns as soon as the plan exceeds it. The three limits (RAM, disk,
cores) show together: showing only one hid the others. On Proxmox the room
comes from the chosen storage, which "pvesm status" already gave.
Two defects found on the way: the generation mark, required of every widget,
silenced each common setting on the Proxmox screen, and "[x1]" vanished,
read as a Rich tag.
Assisted-by: Claude Opus 5
2026-08-24 01:11:09 -04:00
|
|
|
|
disk_note,
|
[ADD] proxmox : un écran pour déployer sur un hôte distant
Le déploiement Proxmox se faisait par vingt questions, l'une après
l'autre, sans jamais voir le plan entier. Il a maintenant le même écran
que QEMU/KVM — catalogue à gauche, plan à droite, totaux dessous — et une
touche pour revenir aux questions. Il n'en redit rien : 690 lignes contre
1 340, tout le reste vient du socle commun.
Ce que Proxmox a en propre est montré AVANT de lancer : le VMID, parce
que l'hôte ne dit « déjà pris » qu'après avoir téléchargé l'image, et
l'adresse qui s'en déduit sur un pont interne. Le stockage et le pont sont
lus sur l'hôte, jamais devinés. L'icône 🗄 distingue l'entrée du menu de
celle qui déploie ici même.
--- EN ---
Deploying on Proxmox meant twenty questions, one after another, never
seeing the whole plan. It now has the same screen as QEMU/KVM — catalogue
left, plan right, totals below — and a key to fall back to the questions.
It repeats none of it: 690 lines against 1,340, everything else comes
from the shared foundation.
What belongs to Proxmox is shown BEFORE launching: the VMID, because the
host only says "already taken" after downloading the image, and the
address derived from it on an internal bridge. Storage and bridge are read
from the host, never guessed. The 🗄 icon tells the menu entry apart from
the one that deploys right here.
Assisted-by: Claude Opus 5
2026-08-23 18:01:28 -04:00
|
|
|
|
entry_key,
|
[ADD] todo : dire la place qui reste sous le plan de déploiement
La ligne de totaux annonçait « ~126 G » sans dire sur quoi : la demande
seule ne dit pas si ça rentre, et on l'apprenait au déploiement. Elle dit
maintenant la demande, ce qui reste et la capacité — « ~126 G / 20 G libres
sur 270 G » — et prévient dès que le plan dépasse. Les trois limites (RAM,
disque, cœurs) s'affichent ensemble : n'en montrer qu'une cachait les
autres. Sur Proxmox, la place vient du stockage choisi, que « pvesm status »
donnait déjà.
Deux défauts trouvés en le faisant : la marque de génération, exigée de tous
les widgets, faisait taire chaque réglage commun de l'écran Proxmox, et
« [x1] » disparaissait, lu comme une balise Rich.
--- EN ---
The totals line said "~126 G" without saying out of what: the demand alone
does not tell whether it fits, and one found out at deploy time. It now says
the demand, what is left and the capacity — "~126 G / 20 G free of 270 G" —
and warns as soon as the plan exceeds it. The three limits (RAM, disk,
cores) show together: showing only one hid the others. On Proxmox the room
comes from the chosen storage, which "pvesm status" already gave.
Two defects found on the way: the generation mark, required of every widget,
silenced each common setting on the Proxmox screen, and "[x1]" vanished,
read as a Rich tag.
Assisted-by: Claude Opus 5
2026-08-24 01:11:09 -04:00
|
|
|
|
gib,
|
[ADD] proxmox : un écran pour déployer sur un hôte distant
Le déploiement Proxmox se faisait par vingt questions, l'une après
l'autre, sans jamais voir le plan entier. Il a maintenant le même écran
que QEMU/KVM — catalogue à gauche, plan à droite, totaux dessous — et une
touche pour revenir aux questions. Il n'en redit rien : 690 lignes contre
1 340, tout le reste vient du socle commun.
Ce que Proxmox a en propre est montré AVANT de lancer : le VMID, parce
que l'hôte ne dit « déjà pris » qu'après avoir téléchargé l'image, et
l'adresse qui s'en déduit sur un pont interne. Le stockage et le pont sont
lus sur l'hôte, jamais devinés. L'icône 🗄 distingue l'entrée du menu de
celle qui déploie ici même.
--- EN ---
Deploying on Proxmox meant twenty questions, one after another, never
seeing the whole plan. It now has the same screen as QEMU/KVM — catalogue
left, plan right, totals below — and a key to fall back to the questions.
It repeats none of it: 690 lines against 1,340, everything else comes
from the shared foundation.
What belongs to Proxmox is shown BEFORE launching: the VMID, because the
host only says "already taken" after downloading the image, and the
address derived from it on an internal bridge. Storage and bridge are read
from the host, never guessed. The 🗄 icon tells the menu entry apart from
the one that deploys right here.
Assisted-by: Claude Opus 5
2026-08-23 18:01:28 -04:00
|
|
|
|
plan_rows,
|
|
|
|
|
|
plan_totals,
|
|
|
|
|
|
res_row_widgets,
|
|
|
|
|
|
t,
|
|
|
|
|
|
)
|
|
|
|
|
|
from script.todo.deploy_form_plan import PlanMixin, preview_screen
|
|
|
|
|
|
|
|
|
|
|
|
# Aucun disque orphelin à craindre : les disques d'un Proxmox distant vivent
|
|
|
|
|
|
# dans un stockage que seul l'hôte connaît, jamais dans /var/lib/libvirt.
|
|
|
|
|
|
PAS_D_ORPHELIN = None
|
|
|
|
|
|
|
[ADD] proxmox : créer le pont manquant depuis l'écran
Sans pont, « qm create » est impossible — et l'écran refusait de déployer
« aucun pont sur l'hôte » sans offrir le moindre moyen d'en avoir un. Une
Proxmox installée SUR Debian n'en a jamais : l'ISO en crée un, pas la
procédure sur Debian.
Deux moments, donc. Avant l'écran, la question se pose dans le terminal, où
l'on peut expliquer les deux voies et montrer ce qui s'exécute. Dans l'écran,
le sélecteur porte « ➕ créer un pont interne vmbr0 (10.10.10.1/24) + NAT » :
la création part dans un fil, l'affichage reste vivant, et le pont créé se
sélectionne tout seul. Elle ne demande rien parce qu'un pont interne ne
touche à aucune interface physique ; un pont sur le LAN déplace l'adresse de
l'hôte et coupe la session, donc il reste manuel.
--- EN ---
With no bridge, "qm create" is impossible — and the screen refused to deploy
"no bridge on the host" without offering any way to get one. A Proxmox
installed ON Debian never has one: the ISO creates it, the Debian procedure
does not.
Two moments, then. Before the screen, the question is asked in the terminal,
where both ways can be explained and the commands shown. In the screen, the
selector carries "➕ create an internal vmbr0 (10.10.10.1/24) + NAT": creation
runs in a thread, the display stays alive, and the new bridge selects itself.
It asks nothing because an internal bridge touches no physical NIC; a bridge
on the LAN moves the host's address and cuts the session, so it stays manual.
Assisted-by: Claude Opus 5
2026-08-24 04:59:13 -04:00
|
|
|
|
# Dernier choix du sélecteur de pont : il ne désigne pas un pont, il en crée
|
|
|
|
|
|
# un. Rapporté — l'écran refusait de déployer « aucun pont sur l'hôte » sans
|
|
|
|
|
|
# offrir le moindre moyen d'en avoir un.
|
|
|
|
|
|
CREER_PONT = "__creer_pont__"
|
|
|
|
|
|
|
[ADD] proxmox : un écran pour déployer sur un hôte distant
Le déploiement Proxmox se faisait par vingt questions, l'une après
l'autre, sans jamais voir le plan entier. Il a maintenant le même écran
que QEMU/KVM — catalogue à gauche, plan à droite, totaux dessous — et une
touche pour revenir aux questions. Il n'en redit rien : 690 lignes contre
1 340, tout le reste vient du socle commun.
Ce que Proxmox a en propre est montré AVANT de lancer : le VMID, parce
que l'hôte ne dit « déjà pris » qu'après avoir téléchargé l'image, et
l'adresse qui s'en déduit sur un pont interne. Le stockage et le pont sont
lus sur l'hôte, jamais devinés. L'icône 🗄 distingue l'entrée du menu de
celle qui déploie ici même.
--- EN ---
Deploying on Proxmox meant twenty questions, one after another, never
seeing the whole plan. It now has the same screen as QEMU/KVM — catalogue
left, plan right, totals below — and a key to fall back to the questions.
It repeats none of it: 690 lines against 1,340, everything else comes
from the shared foundation.
What belongs to Proxmox is shown BEFORE launching: the VMID, because the
host only says "already taken" after downloading the image, and the
address derived from it on an internal bridge. Storage and bridge are read
from the host, never guessed. The 🗄 icon tells the menu entry apart from
the one that deploys right here.
Assisted-by: Claude Opus 5
2026-08-23 18:01:28 -04:00
|
|
|
|
|
|
|
|
|
|
def assign_vmids(rows, used, start, ipconfig):
|
|
|
|
|
|
"""Pose un VMID libre et son adresse sur chaque VM À CRÉER.
|
|
|
|
|
|
|
|
|
|
|
|
Proxmox refuse un VMID déjà pris, et il le dit APRÈS le téléchargement de
|
|
|
|
|
|
l'image : on choisit donc avant, d'après ce que l'hôte déclare. Les VM qui
|
|
|
|
|
|
existent déjà sont sautées — elles ont le leur.
|
|
|
|
|
|
|
|
|
|
|
|
`ipconfig(vmid)` rend la ligne cloud-init : « ip=dhcp » sur un pont qui
|
|
|
|
|
|
donne sur le LAN, une adresse fixe dérivée du VMID sur un pont interne.
|
|
|
|
|
|
"""
|
|
|
|
|
|
pris = {int(v) for v in used or () if str(v).isdigit()}
|
|
|
|
|
|
suivant = max(int(start or 0), 100)
|
|
|
|
|
|
for r in rows:
|
|
|
|
|
|
if r["state"] == "exists":
|
|
|
|
|
|
continue
|
|
|
|
|
|
while suivant in pris:
|
|
|
|
|
|
suivant += 1
|
|
|
|
|
|
pris.add(suivant)
|
|
|
|
|
|
r["vm"]["vmid"] = suivant
|
|
|
|
|
|
r["vm"]["ipconfig"] = ipconfig(suivant) if ipconfig else "ip=dhcp"
|
|
|
|
|
|
suivant += 1
|
|
|
|
|
|
return rows
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def res_label(profile) -> str:
|
|
|
|
|
|
"""Comment le plan nomme le réglage commun choisi."""
|
|
|
|
|
|
return t("custom") if profile == "custom" else f"x{profile}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def build_spec(vms, existants, form):
|
|
|
|
|
|
"""La spec que le déploiement exécutera. Une VM qui existe déjà n'y entre
|
|
|
|
|
|
pas : Proxmox refuserait le VMID, et on ne veut surtout pas l'écraser."""
|
|
|
|
|
|
connus = set(existants)
|
|
|
|
|
|
return {
|
|
|
|
|
|
"host": form["host"],
|
|
|
|
|
|
"storage": form["storage"],
|
|
|
|
|
|
"bridge": form["bridge"],
|
[FIX] proxmox : sh: 1: Syntax error: "(" unexpected
Rapporté. La chaîne, en trois maillons : sur un hôte sans pont, « ip -o link
show type bridge » ne rend RIEN, la sortie ne contient donc que
l'avertissement de ssh sur la clé d'hôte — que le lecteur a pris pour un nom
de pont. « (ED25519) » s'est retrouvé dans « --net0 virtio,bridge=… », enrobé
de « sudo sh -c », et dash a répondu ce que l'utilisateur a lu. Le bruit de
ssh est maintenant retiré à la source, et un pont doit avoir la forme d'un
lien pour en être un.
Éprouvé sur l'hôte réel, VM créée puis détruite : le pont ne montait pas
(ifupdown2 accuse « another instance » quand /run/network manque — un
mensonge), le noyau Debian n'a ni module bridge ni table NAT, et une VM en
adresse fixe n'avait aucun résolveur. Le déploiement écrit désormais un
journal par VM sous ~/.erplibre/proxmox-deploy et en donne le chemin.
--- EN ---
Reported. The chain, in three links: on a host with no bridge, "ip -o link
show type bridge" returns NOTHING, so the output holds only ssh's host-key
warning — which the parser took for a bridge name. "(ED25519)" landed in
"--net0 virtio,bridge=…", wrapped in "sudo sh -c", and dash answered what the
user read. Ssh's noise is now stripped at the source, and a bridge must have
the shape of a link to be one.
Proven on the real host, VM created then destroyed: the bridge would not come
up (ifupdown2 claims "another instance" when /run/network is missing — a lie),
the Debian kernel has neither the bridge module nor the NAT table, and a
statically addressed VM had no resolver at all. Deployment now writes one log
per VM under ~/.erplibre/proxmox-deploy and prints its path.
Assisted-by: Claude Opus 5
2026-08-24 04:17:36 -04:00
|
|
|
|
# Les résolveurs de l'hôte suivent la spec : une VM en adresse fixe
|
|
|
|
|
|
# n'a pas de DNS sans eux.
|
|
|
|
|
|
"nameservers": form.get("nameservers") or (),
|
[ADD] proxmox : un écran pour déployer sur un hôte distant
Le déploiement Proxmox se faisait par vingt questions, l'une après
l'autre, sans jamais voir le plan entier. Il a maintenant le même écran
que QEMU/KVM — catalogue à gauche, plan à droite, totaux dessous — et une
touche pour revenir aux questions. Il n'en redit rien : 690 lignes contre
1 340, tout le reste vient du socle commun.
Ce que Proxmox a en propre est montré AVANT de lancer : le VMID, parce
que l'hôte ne dit « déjà pris » qu'après avoir téléchargé l'image, et
l'adresse qui s'en déduit sur un pont interne. Le stockage et le pont sont
lus sur l'hôte, jamais devinés. L'icône 🗄 distingue l'entrée du menu de
celle qui déploie ici même.
--- EN ---
Deploying on Proxmox meant twenty questions, one after another, never
seeing the whole plan. It now has the same screen as QEMU/KVM — catalogue
left, plan right, totals below — and a key to fall back to the questions.
It repeats none of it: 690 lines against 1,340, everything else comes
from the shared foundation.
What belongs to Proxmox is shown BEFORE launching: the VMID, because the
host only says "already taken" after downloading the image, and the
address derived from it on an internal bridge. Storage and bridge are read
from the host, never guessed. The 🗄 icon tells the menu entry apart from
the one that deploys right here.
Assisted-by: Claude Opus 5
2026-08-23 18:01:28 -04:00
|
|
|
|
"res_label": form["res_label"],
|
|
|
|
|
|
"vms": [vm for vm in vms if vm["name"] not in connus],
|
|
|
|
|
|
"existing": [vm["name"] for vm in vms if vm["name"] in connus],
|
|
|
|
|
|
"ssh_key": form["ssh_key"],
|
|
|
|
|
|
"user": form.get("user") or "erplibre",
|
|
|
|
|
|
"start": form["start"],
|
|
|
|
|
|
"add_ssh_config": form["add_ssh_config"],
|
|
|
|
|
|
"install": form["install"],
|
|
|
|
|
|
"monitor": form["monitor"],
|
|
|
|
|
|
"parallelism": form["parallelism"],
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def run_proxmox_form(ctx, run_app: bool = True):
|
|
|
|
|
|
"""Formulaire Proxmox. Renvoie une spec, None si annulé, {} pour retomber
|
|
|
|
|
|
sur les invites textuelles. `run_app=False` rend l'instance sans la lancer
|
|
|
|
|
|
(tests headless)."""
|
|
|
|
|
|
from textual.app import App, ComposeResult
|
|
|
|
|
|
from textual.containers import Horizontal, Vertical, VerticalScroll
|
|
|
|
|
|
from textual.widgets import (
|
|
|
|
|
|
Button,
|
|
|
|
|
|
Checkbox,
|
|
|
|
|
|
Footer,
|
|
|
|
|
|
Header,
|
|
|
|
|
|
Input,
|
|
|
|
|
|
RadioButton,
|
|
|
|
|
|
RadioSet,
|
|
|
|
|
|
Select,
|
|
|
|
|
|
SelectionList,
|
|
|
|
|
|
Static,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
SELECT_NULL = getattr(Select, "NULL", Select.BLANK)
|
|
|
|
|
|
arches = ctx["arches"]
|
|
|
|
|
|
catalog = ctx["catalog"]
|
|
|
|
|
|
noms_pris = ctx["names"]
|
|
|
|
|
|
vmids_pris = ctx["vmids"]
|
|
|
|
|
|
branches = ctx.get("branches") or ["master"]
|
|
|
|
|
|
profiles = ctx.get("install_profiles") or []
|
|
|
|
|
|
stockages = ctx.get("storages") or []
|
|
|
|
|
|
ponts = ctx.get("bridges") or []
|
[FIX] qemu : ne pas poser ERPLibre sur une VM Proxmox VE
Choisir « Proxmox VE » comme système, c'est demander qu'il soit installé.
L'invite en ligne le savait ; le formulaire posait « ERPLibre + Odoo 18 »,
lui ajoutait les 5 Go réservés au dépôt et annonçait une cible make dans le
guide de la VM. La règle vit maintenant en un seul endroit, les deux
chemins la lisent, et un choix explicite l'emporte toujours sur elle.
Trois défauts trouvés derrière. Une commande par VM n'était retenue que si
DEUX VM différaient : déployée seule, la VM Proxmox retombait sur la
commande commune. Le disque choisi à la main disparaissait quand rien
n'était installé — 60 G demandés, 20 G créés. Et une taille absente des
préréglages marquait la rangée ✎ dès son montage.
--- EN ---
Choosing "Proxmox VE" as the system means asking for it to be installed.
The command-line prompt knew that; the form set "ERPLibre + Odoo 18", added
the 5 GB meant for the repository and advertised a make target in the VM's
guide. The rule now lives in one place, both paths read it, and an explicit
choice always wins over it.
Three defects behind it. A per-VM command was only used when TWO VMs
differed: deployed alone, the Proxmox VM fell back to the common command.
A hand-picked disk size vanished when nothing was installed — 60 G asked,
20 G created. And a size absent from the presets marked its row ✎ on mount.
Assisted-by: Claude Opus 5
2026-08-24 01:34:10 -04:00
|
|
|
|
# {système: (libellé, commande)} — ce qu'un système impose d'installer.
|
|
|
|
|
|
distro_profiles = ctx.get("distro_profiles") or {}
|
|
|
|
|
|
# Les commandes qui ne posent PAS ERPLibre : sa marge disque ne les suit
|
|
|
|
|
|
# pas. DÉDUITES des profils imposés — une seconde clé de contexte à tenir
|
|
|
|
|
|
# en accord avec la première aurait fini par en différer, et la marge
|
|
|
|
|
|
# serait revenue sans qu'on le voie. Jugé sur la commande effective de la
|
|
|
|
|
|
# rangée : un choix explicite compte donc autant que la règle du système.
|
|
|
|
|
|
no_erplibre = {
|
|
|
|
|
|
impose[1].strip() for impose in distro_profiles.values() if impose
|
|
|
|
|
|
}
|
[ADD] proxmox : un écran pour déployer sur un hôte distant
Le déploiement Proxmox se faisait par vingt questions, l'une après
l'autre, sans jamais voir le plan entier. Il a maintenant le même écran
que QEMU/KVM — catalogue à gauche, plan à droite, totaux dessous — et une
touche pour revenir aux questions. Il n'en redit rien : 690 lignes contre
1 340, tout le reste vient du socle commun.
Ce que Proxmox a en propre est montré AVANT de lancer : le VMID, parce
que l'hôte ne dit « déjà pris » qu'après avoir téléchargé l'image, et
l'adresse qui s'en déduit sur un pont interne. Le stockage et le pont sont
lus sur l'hôte, jamais devinés. L'icône 🗄 distingue l'entrée du menu de
celle qui déploie ici même.
--- EN ---
Deploying on Proxmox meant twenty questions, one after another, never
seeing the whole plan. It now has the same screen as QEMU/KVM — catalogue
left, plan right, totals below — and a key to fall back to the questions.
It repeats none of it: 690 lines against 1,340, everything else comes
from the shared foundation.
What belongs to Proxmox is shown BEFORE launching: the VMID, because the
host only says "already taken" after downloading the image, and the
address derived from it on an internal bridge. Storage and bridge are read
from the host, never guessed. The 🗄 icon tells the menu entry apart from
the one that deploys right here.
Assisted-by: Claude Opus 5
2026-08-23 18:01:28 -04:00
|
|
|
|
|
|
|
|
|
|
def entry_label(e):
|
|
|
|
|
|
return f"{e['distro']} {e['version']} [{e['arch']}] {e['name']}"
|
|
|
|
|
|
|
|
|
|
|
|
class ProxmoxForm(PlanMixin, App):
|
|
|
|
|
|
TITLE = t("Deploy one or more ERPLibre VMs on Proxmox VE!")
|
|
|
|
|
|
BINDINGS = [
|
|
|
|
|
|
("f5", "deploy", t("Deploy")),
|
|
|
|
|
|
("f4", "clear_vm", t("Reset VM")),
|
|
|
|
|
|
("f3", "preview", t("Preview")),
|
|
|
|
|
|
("f6", "select_all", t("All")),
|
|
|
|
|
|
("f8", "select_none", t("None")),
|
|
|
|
|
|
("escape", "cancel", t("Cancel")),
|
|
|
|
|
|
]
|
|
|
|
|
|
# Le socle porte la mise en page et les modales ; ne reste ici que ce
|
|
|
|
|
|
# qui nomme les widgets propres à Proxmox.
|
|
|
|
|
|
CSS = (
|
|
|
|
|
|
CSS_BASE
|
|
|
|
|
|
+ """
|
|
|
|
|
|
SelectionList { height: 10; border: solid $panel; }
|
|
|
|
|
|
RadioSet { height: auto; layout: horizontal; }
|
|
|
|
|
|
.vmrow Select.vmbranch { width: 34; }
|
|
|
|
|
|
#hostline { height: 1; color: $accent; padding: 0 1; }
|
|
|
|
|
|
"""
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
|
|
super().__init__()
|
|
|
|
|
|
self.arch = ctx.get("native") or arches[0]
|
|
|
|
|
|
self.profile = "1"
|
|
|
|
|
|
self.custom = {}
|
|
|
|
|
|
self.overrides = {}
|
|
|
|
|
|
self.locked = set()
|
|
|
|
|
|
self.copies = {}
|
|
|
|
|
|
self.rows = []
|
|
|
|
|
|
self.vms = []
|
|
|
|
|
|
self.result = None
|
|
|
|
|
|
# Génération des widgets de rangée : un message qui arrive d'un
|
|
|
|
|
|
# jeu périmé ne doit pas être pris pour une saisie.
|
|
|
|
|
|
self._gen = 0
|
|
|
|
|
|
self._shown_ids = ()
|
|
|
|
|
|
self._syncing = False
|
[ADD] proxmox : créer le pont manquant depuis l'écran
Sans pont, « qm create » est impossible — et l'écran refusait de déployer
« aucun pont sur l'hôte » sans offrir le moindre moyen d'en avoir un. Une
Proxmox installée SUR Debian n'en a jamais : l'ISO en crée un, pas la
procédure sur Debian.
Deux moments, donc. Avant l'écran, la question se pose dans le terminal, où
l'on peut expliquer les deux voies et montrer ce qui s'exécute. Dans l'écran,
le sélecteur porte « ➕ créer un pont interne vmbr0 (10.10.10.1/24) + NAT » :
la création part dans un fil, l'affichage reste vivant, et le pont créé se
sélectionne tout seul. Elle ne demande rien parce qu'un pont interne ne
touche à aucune interface physique ; un pont sur le LAN déplace l'adresse de
l'hôte et coupe la session, donc il reste manuel.
--- EN ---
With no bridge, "qm create" is impossible — and the screen refused to deploy
"no bridge on the host" without offering any way to get one. A Proxmox
installed ON Debian never has one: the ISO creates it, the Debian procedure
does not.
Two moments, then. Before the screen, the question is asked in the terminal,
where both ways can be explained and the commands shown. In the screen, the
selector carries "➕ create an internal vmbr0 (10.10.10.1/24) + NAT": creation
runs in a thread, the display stays alive, and the new bridge selects itself.
It asks nothing because an internal bridge touches no physical NIC; a bridge
on the LAN moves the host's address and cuts the session, so it stays manual.
Assisted-by: Claude Opus 5
2026-08-24 04:59:13 -04:00
|
|
|
|
# La liste des ponts GRANDIT : l'écran sait en créer un.
|
|
|
|
|
|
self._ponts = list(ponts)
|
[ADD] proxmox : un écran pour déployer sur un hôte distant
Le déploiement Proxmox se faisait par vingt questions, l'une après
l'autre, sans jamais voir le plan entier. Il a maintenant le même écran
que QEMU/KVM — catalogue à gauche, plan à droite, totaux dessous — et une
touche pour revenir aux questions. Il n'en redit rien : 690 lignes contre
1 340, tout le reste vient du socle commun.
Ce que Proxmox a en propre est montré AVANT de lancer : le VMID, parce
que l'hôte ne dit « déjà pris » qu'après avoir téléchargé l'image, et
l'adresse qui s'en déduit sur un pont interne. Le stockage et le pont sont
lus sur l'hôte, jamais devinés. L'icône 🗄 distingue l'entrée du menu de
celle qui déploie ici même.
--- EN ---
Deploying on Proxmox meant twenty questions, one after another, never
seeing the whole plan. It now has the same screen as QEMU/KVM — catalogue
left, plan right, totals below — and a key to fall back to the questions.
It repeats none of it: 690 lines against 1,340, everything else comes
from the shared foundation.
What belongs to Proxmox is shown BEFORE launching: the VMID, because the
host only says "already taken" after downloading the image, and the
address derived from it on an internal bridge. Storage and bridge are read
from the host, never guessed. The 🗄 icon tells the menu entry apart from
the one that deploys right here.
Assisted-by: Claude Opus 5
2026-08-23 18:01:28 -04:00
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------- #
|
|
|
|
|
|
# L'écran
|
|
|
|
|
|
# ---------------------------------------------------------------- #
|
|
|
|
|
|
def compose(self) -> ComposeResult:
|
|
|
|
|
|
yield Header()
|
|
|
|
|
|
hote = ctx["host"].get("label") or ctx["host"]["target"]
|
|
|
|
|
|
yield Static(
|
|
|
|
|
|
f" {t('Proxmox host')} : {hote}"
|
|
|
|
|
|
f" {t('node')} : {ctx.get('node') or '?'}",
|
|
|
|
|
|
id="hostline",
|
|
|
|
|
|
)
|
|
|
|
|
|
with Horizontal(id="body"):
|
|
|
|
|
|
with VerticalScroll(id="fields"):
|
|
|
|
|
|
yield Static(t("Architecture"), classes="grouptitle")
|
|
|
|
|
|
with RadioSet(id="f_arch"):
|
|
|
|
|
|
for a in arches:
|
|
|
|
|
|
label = a if a != "all" else t("all archs")
|
|
|
|
|
|
yield RadioButton(label, value=a == self.arch)
|
|
|
|
|
|
yield Static(t("Catalog"), classes="grouptitle")
|
|
|
|
|
|
yield SelectionList(id="f_catalog")
|
|
|
|
|
|
yield Static(
|
|
|
|
|
|
t("Resources — applied to ALL VMs"),
|
|
|
|
|
|
classes="grouptitle",
|
|
|
|
|
|
)
|
|
|
|
|
|
with RadioSet(id="f_profile"):
|
|
|
|
|
|
for label in ("x1", "x2", "x3", "x4"):
|
|
|
|
|
|
yield RadioButton(label, value=label == "x1")
|
|
|
|
|
|
yield RadioButton(t("custom"))
|
|
|
|
|
|
# Les mêmes trois ressources qu'ailleurs, montées par la
|
|
|
|
|
|
# même fabrique : « libre… » révèle la saisie du dessous.
|
|
|
|
|
|
for champ, presets, etiquette in (
|
|
|
|
|
|
("vcpus", ctx["cpu_presets"], t("vCPU")),
|
|
|
|
|
|
("ram", ctx["ram_presets"], t("RAM: 2048 or 8G")),
|
|
|
|
|
|
("disk", ctx["disk_presets"], t("Disk")),
|
|
|
|
|
|
):
|
|
|
|
|
|
yield Select(
|
|
|
|
|
|
[
|
|
|
|
|
|
(
|
|
|
|
|
|
(
|
|
|
|
|
|
f"{v // 1024}G"
|
|
|
|
|
|
if champ == "ram"
|
|
|
|
|
|
else str(v)
|
|
|
|
|
|
),
|
|
|
|
|
|
v,
|
|
|
|
|
|
)
|
|
|
|
|
|
for v in presets
|
|
|
|
|
|
]
|
|
|
|
|
|
+ [(t("free value…"), FREE)],
|
|
|
|
|
|
prompt=etiquette,
|
|
|
|
|
|
id=RES_FIELDS[champ][0][1:],
|
|
|
|
|
|
disabled=True,
|
|
|
|
|
|
)
|
|
|
|
|
|
yield Input(
|
|
|
|
|
|
placeholder=etiquette,
|
|
|
|
|
|
id=RES_FIELDS[champ][1][1:],
|
|
|
|
|
|
classes="freeval",
|
|
|
|
|
|
disabled=True,
|
|
|
|
|
|
)
|
|
|
|
|
|
yield Static(t("Proxmox VE"), classes="grouptitle")
|
|
|
|
|
|
yield Select(
|
|
|
|
|
|
[(s, s) for s in stockages],
|
|
|
|
|
|
value=(
|
|
|
|
|
|
ctx.get("storage")
|
|
|
|
|
|
or (stockages[0] if stockages else SELECT_NULL)
|
|
|
|
|
|
),
|
|
|
|
|
|
prompt=t("Storage"),
|
|
|
|
|
|
allow_blank=not stockages,
|
|
|
|
|
|
id="f_storage",
|
|
|
|
|
|
)
|
|
|
|
|
|
yield Select(
|
[ADD] proxmox : créer le pont manquant depuis l'écran
Sans pont, « qm create » est impossible — et l'écran refusait de déployer
« aucun pont sur l'hôte » sans offrir le moindre moyen d'en avoir un. Une
Proxmox installée SUR Debian n'en a jamais : l'ISO en crée un, pas la
procédure sur Debian.
Deux moments, donc. Avant l'écran, la question se pose dans le terminal, où
l'on peut expliquer les deux voies et montrer ce qui s'exécute. Dans l'écran,
le sélecteur porte « ➕ créer un pont interne vmbr0 (10.10.10.1/24) + NAT » :
la création part dans un fil, l'affichage reste vivant, et le pont créé se
sélectionne tout seul. Elle ne demande rien parce qu'un pont interne ne
touche à aucune interface physique ; un pont sur le LAN déplace l'adresse de
l'hôte et coupe la session, donc il reste manuel.
--- EN ---
With no bridge, "qm create" is impossible — and the screen refused to deploy
"no bridge on the host" without offering any way to get one. A Proxmox
installed ON Debian never has one: the ISO creates it, the Debian procedure
does not.
Two moments, then. Before the screen, the question is asked in the terminal,
where both ways can be explained and the commands shown. In the screen, the
selector carries "➕ create an internal vmbr0 (10.10.10.1/24) + NAT": creation
runs in a thread, the display stays alive, and the new bridge selects itself.
It asks nothing because an internal bridge touches no physical NIC; a bridge
on the LAN moves the host's address and cuts the session, so it stays manual.
Assisted-by: Claude Opus 5
2026-08-24 04:59:13 -04:00
|
|
|
|
self._choix_ponts(),
|
[ADD] proxmox : un écran pour déployer sur un hôte distant
Le déploiement Proxmox se faisait par vingt questions, l'une après
l'autre, sans jamais voir le plan entier. Il a maintenant le même écran
que QEMU/KVM — catalogue à gauche, plan à droite, totaux dessous — et une
touche pour revenir aux questions. Il n'en redit rien : 690 lignes contre
1 340, tout le reste vient du socle commun.
Ce que Proxmox a en propre est montré AVANT de lancer : le VMID, parce
que l'hôte ne dit « déjà pris » qu'après avoir téléchargé l'image, et
l'adresse qui s'en déduit sur un pont interne. Le stockage et le pont sont
lus sur l'hôte, jamais devinés. L'icône 🗄 distingue l'entrée du menu de
celle qui déploie ici même.
--- EN ---
Deploying on Proxmox meant twenty questions, one after another, never
seeing the whole plan. It now has the same screen as QEMU/KVM — catalogue
left, plan right, totals below — and a key to fall back to the questions.
It repeats none of it: 690 lines against 1,340, everything else comes
from the shared foundation.
What belongs to Proxmox is shown BEFORE launching: the VMID, because the
host only says "already taken" after downloading the image, and the
address derived from it on an internal bridge. Storage and bridge are read
from the host, never guessed. The 🗄 icon tells the menu entry apart from
the one that deploys right here.
Assisted-by: Claude Opus 5
2026-08-23 18:01:28 -04:00
|
|
|
|
value=(
|
|
|
|
|
|
ctx.get("bridge")
|
|
|
|
|
|
or (ponts[0] if ponts else SELECT_NULL)
|
|
|
|
|
|
),
|
|
|
|
|
|
prompt=t("Bridge"),
|
|
|
|
|
|
allow_blank=not ponts,
|
|
|
|
|
|
id="f_bridge",
|
|
|
|
|
|
)
|
|
|
|
|
|
yield Static(f" {t('First VMID')}")
|
|
|
|
|
|
yield Input(
|
|
|
|
|
|
value=str(ctx.get("next_vmid") or 100),
|
|
|
|
|
|
placeholder="100",
|
|
|
|
|
|
id="f_vmid",
|
|
|
|
|
|
)
|
|
|
|
|
|
yield Static(t("Access"), classes="grouptitle")
|
|
|
|
|
|
yield Static(f" {t('SSH public key')}")
|
|
|
|
|
|
yield Input(
|
|
|
|
|
|
value=ctx.get("ssh_key") or "",
|
|
|
|
|
|
placeholder="~/.ssh/id_ed25519.pub",
|
|
|
|
|
|
id="f_key",
|
|
|
|
|
|
)
|
|
|
|
|
|
yield Checkbox(
|
|
|
|
|
|
t("Start the VM after creating it"),
|
|
|
|
|
|
value=True,
|
|
|
|
|
|
id="f_start",
|
|
|
|
|
|
)
|
|
|
|
|
|
yield Checkbox(
|
|
|
|
|
|
t("Add an entry to ~/.ssh/config"),
|
|
|
|
|
|
value=True,
|
|
|
|
|
|
id="f_sshcfg",
|
|
|
|
|
|
)
|
[UPD] todo : nommer la case d'installation par ce qu'elle commande
« Installer ERPLibre » commandait TOUTE installation, l'hyperviseur Proxmox
VE compris : décochée, une VM Proxmox restait une Debian nue sans que rien
ne l'explique. Elle devient « Installer un logiciel dans la VM », passe sous
le type de VM — juste avant les sections qu'elle commande — et le suivi la
quitte, puisqu'il regarde la VM arriver même quand rien ne s'installe.
Ce qu'elle rend sans effet se grise, titre compris. Trois états et non deux :
sans installation mais AVEC un bureau, le magasin d'applications et les
outils de la phase « avant » servent encore ; seuls ceux qui vivent dans le
dépôt s'éteignent. Griser en bloc aurait menti autant que de tout laisser.
--- EN ---
"Install ERPLibre" commanded EVERY install, the Proxmox VE hypervisor
included: unticked, a Proxmox VM stayed a bare Debian with nothing to explain
it. It becomes "Install software in the VM", moves under the VM type — right
before the sections it commands — and the dashboard leaves it, since that
watches the VM arrive even when nothing is installed.
What it makes ineffective now greys out, title included. Three states, not
two: with no install but WITH a desktop, the application store and the
"before" tools still act; only those living in the repository go dark.
Greying the lot would have lied as much as greying nothing.
Assisted-by: Claude Opus 5
2026-08-24 02:32:09 -04:00
|
|
|
|
# La case commande TOUTE installation — ERPLibre, Odoo,
|
|
|
|
|
|
# mais aussi l'hyperviseur Proxmox VE d'une VM imbriquée.
|
|
|
|
|
|
# Nommée « ERPLibre », elle laissait croire qu'un système
|
|
|
|
|
|
# Proxmox s'installerait quand même.
|
|
|
|
|
|
yield Static(
|
|
|
|
|
|
t("Installation"),
|
|
|
|
|
|
id="t_install",
|
|
|
|
|
|
classes="grouptitle",
|
|
|
|
|
|
)
|
[ADD] proxmox : un écran pour déployer sur un hôte distant
Le déploiement Proxmox se faisait par vingt questions, l'une après
l'autre, sans jamais voir le plan entier. Il a maintenant le même écran
que QEMU/KVM — catalogue à gauche, plan à droite, totaux dessous — et une
touche pour revenir aux questions. Il n'en redit rien : 690 lignes contre
1 340, tout le reste vient du socle commun.
Ce que Proxmox a en propre est montré AVANT de lancer : le VMID, parce
que l'hôte ne dit « déjà pris » qu'après avoir téléchargé l'image, et
l'adresse qui s'en déduit sur un pont interne. Le stockage et le pont sont
lus sur l'hôte, jamais devinés. L'icône 🗄 distingue l'entrée du menu de
celle qui déploie ici même.
--- EN ---
Deploying on Proxmox meant twenty questions, one after another, never
seeing the whole plan. It now has the same screen as QEMU/KVM — catalogue
left, plan right, totals below — and a key to fall back to the questions.
It repeats none of it: 690 lines against 1,340, everything else comes
from the shared foundation.
What belongs to Proxmox is shown BEFORE launching: the VMID, because the
host only says "already taken" after downloading the image, and the
address derived from it on an internal bridge. Storage and bridge are read
from the host, never guessed. The 🗄 icon tells the menu entry apart from
the one that deploys right here.
Assisted-by: Claude Opus 5
2026-08-23 18:01:28 -04:00
|
|
|
|
yield Checkbox(
|
[UPD] todo : nommer la case d'installation par ce qu'elle commande
« Installer ERPLibre » commandait TOUTE installation, l'hyperviseur Proxmox
VE compris : décochée, une VM Proxmox restait une Debian nue sans que rien
ne l'explique. Elle devient « Installer un logiciel dans la VM », passe sous
le type de VM — juste avant les sections qu'elle commande — et le suivi la
quitte, puisqu'il regarde la VM arriver même quand rien ne s'installe.
Ce qu'elle rend sans effet se grise, titre compris. Trois états et non deux :
sans installation mais AVEC un bureau, le magasin d'applications et les
outils de la phase « avant » servent encore ; seuls ceux qui vivent dans le
dépôt s'éteignent. Griser en bloc aurait menti autant que de tout laisser.
--- EN ---
"Install ERPLibre" commanded EVERY install, the Proxmox VE hypervisor
included: unticked, a Proxmox VM stayed a bare Debian with nothing to explain
it. It becomes "Install software in the VM", moves under the VM type — right
before the sections it commands — and the dashboard leaves it, since that
watches the VM arrive even when nothing is installed.
What it makes ineffective now greys out, title included. Three states, not
two: with no install but WITH a desktop, the application store and the
"before" tools still act; only those living in the repository go dark.
Greying the lot would have lied as much as greying nothing.
Assisted-by: Claude Opus 5
2026-08-24 02:32:09 -04:00
|
|
|
|
t("Install software in the VM"),
|
|
|
|
|
|
value=True,
|
|
|
|
|
|
id="f_install",
|
[ADD] proxmox : un écran pour déployer sur un hôte distant
Le déploiement Proxmox se faisait par vingt questions, l'une après
l'autre, sans jamais voir le plan entier. Il a maintenant le même écran
que QEMU/KVM — catalogue à gauche, plan à droite, totaux dessous — et une
touche pour revenir aux questions. Il n'en redit rien : 690 lignes contre
1 340, tout le reste vient du socle commun.
Ce que Proxmox a en propre est montré AVANT de lancer : le VMID, parce
que l'hôte ne dit « déjà pris » qu'après avoir téléchargé l'image, et
l'adresse qui s'en déduit sur un pont interne. Le stockage et le pont sont
lus sur l'hôte, jamais devinés. L'icône 🗄 distingue l'entrée du menu de
celle qui déploie ici même.
--- EN ---
Deploying on Proxmox meant twenty questions, one after another, never
seeing the whole plan. It now has the same screen as QEMU/KVM — catalogue
left, plan right, totals below — and a key to fall back to the questions.
It repeats none of it: 690 lines against 1,340, everything else comes
from the shared foundation.
What belongs to Proxmox is shown BEFORE launching: the VMID, because the
host only says "already taken" after downloading the image, and the
address derived from it on an internal bridge. Storage and bridge are read
from the host, never guessed. The 🗄 icon tells the menu entry apart from
the one that deploys right here.
Assisted-by: Claude Opus 5
2026-08-23 18:01:28 -04:00
|
|
|
|
)
|
|
|
|
|
|
yield Select(
|
|
|
|
|
|
[(lbl, i) for i, (lbl, _c) in enumerate(profiles)],
|
|
|
|
|
|
value=0 if profiles else SELECT_NULL,
|
|
|
|
|
|
allow_blank=not profiles,
|
|
|
|
|
|
id="f_profile_install",
|
|
|
|
|
|
)
|
|
|
|
|
|
yield Select(
|
|
|
|
|
|
[(b, b) for b in branches],
|
|
|
|
|
|
value=branches[0],
|
|
|
|
|
|
allow_blank=False,
|
|
|
|
|
|
id="f_branch",
|
|
|
|
|
|
)
|
[UPD] todo : nommer la case d'installation par ce qu'elle commande
« Installer ERPLibre » commandait TOUTE installation, l'hyperviseur Proxmox
VE compris : décochée, une VM Proxmox restait une Debian nue sans que rien
ne l'explique. Elle devient « Installer un logiciel dans la VM », passe sous
le type de VM — juste avant les sections qu'elle commande — et le suivi la
quitte, puisqu'il regarde la VM arriver même quand rien ne s'installe.
Ce qu'elle rend sans effet se grise, titre compris. Trois états et non deux :
sans installation mais AVEC un bureau, le magasin d'applications et les
outils de la phase « avant » servent encore ; seuls ceux qui vivent dans le
dépôt s'éteignent. Griser en bloc aurait menti autant que de tout laisser.
--- EN ---
"Install ERPLibre" commanded EVERY install, the Proxmox VE hypervisor
included: unticked, a Proxmox VM stayed a bare Debian with nothing to explain
it. It becomes "Install software in the VM", moves under the VM type — right
before the sections it commands — and the dashboard leaves it, since that
watches the VM arrive even when nothing is installed.
What it makes ineffective now greys out, title included. Three states, not
two: with no install but WITH a desktop, the application store and the
"before" tools still act; only those living in the repository go dark.
Greying the lot would have lied as much as greying nothing.
Assisted-by: Claude Opus 5
2026-08-24 02:32:09 -04:00
|
|
|
|
# Hors de la section « Installation » : le suivi regarde la
|
|
|
|
|
|
# VM ARRIVER, même quand rien ne s'installe. Rangé dedans,
|
|
|
|
|
|
# il se serait grisé avec elle.
|
|
|
|
|
|
yield Static(
|
|
|
|
|
|
t("Monitoring and parallelism"),
|
|
|
|
|
|
classes="grouptitle",
|
|
|
|
|
|
)
|
[ADD] proxmox : un écran pour déployer sur un hôte distant
Le déploiement Proxmox se faisait par vingt questions, l'une après
l'autre, sans jamais voir le plan entier. Il a maintenant le même écran
que QEMU/KVM — catalogue à gauche, plan à droite, totaux dessous — et une
touche pour revenir aux questions. Il n'en redit rien : 690 lignes contre
1 340, tout le reste vient du socle commun.
Ce que Proxmox a en propre est montré AVANT de lancer : le VMID, parce
que l'hôte ne dit « déjà pris » qu'après avoir téléchargé l'image, et
l'adresse qui s'en déduit sur un pont interne. Le stockage et le pont sont
lus sur l'hôte, jamais devinés. L'icône 🗄 distingue l'entrée du menu de
celle qui déploie ici même.
--- EN ---
Deploying on Proxmox meant twenty questions, one after another, never
seeing the whole plan. It now has the same screen as QEMU/KVM — catalogue
left, plan right, totals below — and a key to fall back to the questions.
It repeats none of it: 690 lines against 1,340, everything else comes
from the shared foundation.
What belongs to Proxmox is shown BEFORE launching: the VMID, because the
host only says "already taken" after downloading the image, and the
address derived from it on an internal bridge. Storage and bridge are read
from the host, never guessed. The 🗄 icon tells the menu entry apart from
the one that deploys right here.
Assisted-by: Claude Opus 5
2026-08-23 18:01:28 -04:00
|
|
|
|
yield Checkbox(
|
|
|
|
|
|
t("Follow the installation (dashboard)"),
|
|
|
|
|
|
value=True,
|
|
|
|
|
|
id="f_monitor",
|
|
|
|
|
|
)
|
[UPD] todo : nommer la case d'installation par ce qu'elle commande
« Installer ERPLibre » commandait TOUTE installation, l'hyperviseur Proxmox
VE compris : décochée, une VM Proxmox restait une Debian nue sans que rien
ne l'explique. Elle devient « Installer un logiciel dans la VM », passe sous
le type de VM — juste avant les sections qu'elle commande — et le suivi la
quitte, puisqu'il regarde la VM arriver même quand rien ne s'installe.
Ce qu'elle rend sans effet se grise, titre compris. Trois états et non deux :
sans installation mais AVEC un bureau, le magasin d'applications et les
outils de la phase « avant » servent encore ; seuls ceux qui vivent dans le
dépôt s'éteignent. Griser en bloc aurait menti autant que de tout laisser.
--- EN ---
"Install ERPLibre" commanded EVERY install, the Proxmox VE hypervisor
included: unticked, a Proxmox VM stayed a bare Debian with nothing to explain
it. It becomes "Install software in the VM", moves under the VM type — right
before the sections it commands — and the dashboard leaves it, since that
watches the VM arrive even when nothing is installed.
What it makes ineffective now greys out, title included. Three states, not
two: with no install but WITH a desktop, the application store and the
"before" tools still act; only those living in the repository go dark.
Greying the lot would have lied as much as greying nothing.
Assisted-by: Claude Opus 5
2026-08-24 02:32:09 -04:00
|
|
|
|
yield Static(f" {t('Parallelism')}")
|
[ADD] proxmox : un écran pour déployer sur un hôte distant
Le déploiement Proxmox se faisait par vingt questions, l'une après
l'autre, sans jamais voir le plan entier. Il a maintenant le même écran
que QEMU/KVM — catalogue à gauche, plan à droite, totaux dessous — et une
touche pour revenir aux questions. Il n'en redit rien : 690 lignes contre
1 340, tout le reste vient du socle commun.
Ce que Proxmox a en propre est montré AVANT de lancer : le VMID, parce
que l'hôte ne dit « déjà pris » qu'après avoir téléchargé l'image, et
l'adresse qui s'en déduit sur un pont interne. Le stockage et le pont sont
lus sur l'hôte, jamais devinés. L'icône 🗄 distingue l'entrée du menu de
celle qui déploie ici même.
--- EN ---
Deploying on Proxmox meant twenty questions, one after another, never
seeing the whole plan. It now has the same screen as QEMU/KVM — catalogue
left, plan right, totals below — and a key to fall back to the questions.
It repeats none of it: 690 lines against 1,340, everything else comes
from the shared foundation.
What belongs to Proxmox is shown BEFORE launching: the VMID, because the
host only says "already taken" after downloading the image, and the
address derived from it on an internal bridge. Storage and bridge are read
from the host, never guessed. The 🗄 icon tells the menu entry apart from
the one that deploys right here.
Assisted-by: Claude Opus 5
2026-08-23 18:01:28 -04:00
|
|
|
|
yield Select(
|
|
|
|
|
|
[(str(n), n) for n in (1, 2, 3, 4)],
|
|
|
|
|
|
value=1,
|
|
|
|
|
|
allow_blank=False,
|
|
|
|
|
|
id="f_par",
|
|
|
|
|
|
)
|
|
|
|
|
|
with Vertical(id="right"):
|
|
|
|
|
|
yield VerticalScroll(id="plan")
|
|
|
|
|
|
yield Static("", id="totals")
|
|
|
|
|
|
with Horizontal(id="actions"):
|
|
|
|
|
|
yield Button(t("Deploy"), variant="primary", id="go")
|
|
|
|
|
|
yield Button(t("Text prompts"), id="prompts")
|
|
|
|
|
|
yield Button(t("Cancel"), id="no")
|
|
|
|
|
|
yield Footer()
|
|
|
|
|
|
|
[ADD] proxmox : créer le pont manquant depuis l'écran
Sans pont, « qm create » est impossible — et l'écran refusait de déployer
« aucun pont sur l'hôte » sans offrir le moindre moyen d'en avoir un. Une
Proxmox installée SUR Debian n'en a jamais : l'ISO en crée un, pas la
procédure sur Debian.
Deux moments, donc. Avant l'écran, la question se pose dans le terminal, où
l'on peut expliquer les deux voies et montrer ce qui s'exécute. Dans l'écran,
le sélecteur porte « ➕ créer un pont interne vmbr0 (10.10.10.1/24) + NAT » :
la création part dans un fil, l'affichage reste vivant, et le pont créé se
sélectionne tout seul. Elle ne demande rien parce qu'un pont interne ne
touche à aucune interface physique ; un pont sur le LAN déplace l'adresse de
l'hôte et coupe la session, donc il reste manuel.
--- EN ---
With no bridge, "qm create" is impossible — and the screen refused to deploy
"no bridge on the host" without offering any way to get one. A Proxmox
installed ON Debian never has one: the ISO creates it, the Debian procedure
does not.
Two moments, then. Before the screen, the question is asked in the terminal,
where both ways can be explained and the commands shown. In the screen, the
selector carries "➕ create an internal vmbr0 (10.10.10.1/24) + NAT": creation
runs in a thread, the display stays alive, and the new bridge selects itself.
It asks nothing because an internal bridge touches no physical NIC; a bridge
on the LAN moves the host's address and cuts the session, so it stays manual.
Assisted-by: Claude Opus 5
2026-08-24 04:59:13 -04:00
|
|
|
|
def _choix_ponts(self):
|
|
|
|
|
|
"""Les ponts de l'hôte, plus « en créer un ».
|
|
|
|
|
|
|
|
|
|
|
|
L'entrée de création reste offerte même quand des ponts existent :
|
|
|
|
|
|
sur un hôte qui n'en a qu'un, sur le LAN, on peut vouloir un
|
|
|
|
|
|
réseau interne pour un parc d'essai."""
|
|
|
|
|
|
choix = [(b, b) for b in self._ponts]
|
|
|
|
|
|
if ctx.get("make_bridge"):
|
|
|
|
|
|
nom, cidr = ctx.get("internal_bridge") or ("vmbr0", "")
|
|
|
|
|
|
choix.append(
|
|
|
|
|
|
(
|
|
|
|
|
|
f"➕ {t('create an internal')} {nom} ({cidr}) + NAT",
|
|
|
|
|
|
CREER_PONT,
|
|
|
|
|
|
)
|
|
|
|
|
|
)
|
|
|
|
|
|
return choix
|
|
|
|
|
|
|
|
|
|
|
|
def _creer_pont(self) -> None:
|
|
|
|
|
|
"""Crée le pont sur l'hôte, dans un FIL : l'appel dure des
|
|
|
|
|
|
secondes, et l'écran doit rester vivant pendant ce temps."""
|
|
|
|
|
|
self.notify(t("Creating the bridge on the host…"))
|
|
|
|
|
|
|
|
|
|
|
|
def travail():
|
|
|
|
|
|
nom, raison = ctx["make_bridge"]()
|
|
|
|
|
|
self.call_from_thread(self._pont_cree, nom, raison)
|
|
|
|
|
|
|
|
|
|
|
|
self.run_worker(travail, thread=True)
|
|
|
|
|
|
|
|
|
|
|
|
def _pont_cree(self, nom, raison) -> None:
|
|
|
|
|
|
"""Retour du fil. Le sélecteur est remis d'aplomb dans les DEUX
|
|
|
|
|
|
cas : laissé sur « créer », il ne désignerait aucun pont."""
|
|
|
|
|
|
selecteur = self.query_one("#f_bridge", Select)
|
|
|
|
|
|
if not nom:
|
|
|
|
|
|
self.notify(
|
|
|
|
|
|
f"{t('The bridge did not come up.')} {raison}",
|
|
|
|
|
|
severity="error",
|
|
|
|
|
|
timeout=12,
|
|
|
|
|
|
)
|
|
|
|
|
|
selecteur.value = (
|
|
|
|
|
|
self._ponts[0] if self._ponts else SELECT_NULL
|
|
|
|
|
|
)
|
|
|
|
|
|
return
|
|
|
|
|
|
if nom not in self._ponts:
|
|
|
|
|
|
self._ponts.append(nom)
|
|
|
|
|
|
self._syncing = True
|
|
|
|
|
|
selecteur.set_options(self._choix_ponts())
|
|
|
|
|
|
selecteur.value = nom
|
|
|
|
|
|
self._syncing = False
|
|
|
|
|
|
self.notify(f"✓ {nom}")
|
|
|
|
|
|
self._refresh_after()
|
|
|
|
|
|
|
[ADD] proxmox : un écran pour déployer sur un hôte distant
Le déploiement Proxmox se faisait par vingt questions, l'une après
l'autre, sans jamais voir le plan entier. Il a maintenant le même écran
que QEMU/KVM — catalogue à gauche, plan à droite, totaux dessous — et une
touche pour revenir aux questions. Il n'en redit rien : 690 lignes contre
1 340, tout le reste vient du socle commun.
Ce que Proxmox a en propre est montré AVANT de lancer : le VMID, parce
que l'hôte ne dit « déjà pris » qu'après avoir téléchargé l'image, et
l'adresse qui s'en déduit sur un pont interne. Le stockage et le pont sont
lus sur l'hôte, jamais devinés. L'icône 🗄 distingue l'entrée du menu de
celle qui déploie ici même.
--- EN ---
Deploying on Proxmox meant twenty questions, one after another, never
seeing the whole plan. It now has the same screen as QEMU/KVM — catalogue
left, plan right, totals below — and a key to fall back to the questions.
It repeats none of it: 690 lines against 1,340, everything else comes
from the shared foundation.
What belongs to Proxmox is shown BEFORE launching: the VMID, because the
host only says "already taken" after downloading the image, and the
address derived from it on an internal bridge. Storage and bridge are read
from the host, never guessed. The 🗄 icon tells the menu entry apart from
the one that deploys right here.
Assisted-by: Claude Opus 5
2026-08-23 18:01:28 -04:00
|
|
|
|
def on_mount(self) -> None:
|
|
|
|
|
|
self._reload_catalog()
|
[UPD] todo : nommer la case d'installation par ce qu'elle commande
« Installer ERPLibre » commandait TOUTE installation, l'hyperviseur Proxmox
VE compris : décochée, une VM Proxmox restait une Debian nue sans que rien
ne l'explique. Elle devient « Installer un logiciel dans la VM », passe sous
le type de VM — juste avant les sections qu'elle commande — et le suivi la
quitte, puisqu'il regarde la VM arriver même quand rien ne s'installe.
Ce qu'elle rend sans effet se grise, titre compris. Trois états et non deux :
sans installation mais AVEC un bureau, le magasin d'applications et les
outils de la phase « avant » servent encore ; seuls ceux qui vivent dans le
dépôt s'éteignent. Griser en bloc aurait menti autant que de tout laisser.
--- EN ---
"Install ERPLibre" commanded EVERY install, the Proxmox VE hypervisor
included: unticked, a Proxmox VM stayed a bare Debian with nothing to explain
it. It becomes "Install software in the VM", moves under the VM type — right
before the sections it commands — and the dashboard leaves it, since that
watches the VM arrive even when nothing is installed.
What it makes ineffective now greys out, title included. Three states, not
two: with no install but WITH a desktop, the application store and the
"before" tools still act; only those living in the repository go dark.
Greying the lot would have lied as much as greying nothing.
Assisted-by: Claude Opus 5
2026-08-24 02:32:09 -04:00
|
|
|
|
self._sync_install_deps()
|
[ADD] proxmox : un écran pour déployer sur un hôte distant
Le déploiement Proxmox se faisait par vingt questions, l'une après
l'autre, sans jamais voir le plan entier. Il a maintenant le même écran
que QEMU/KVM — catalogue à gauche, plan à droite, totaux dessous — et une
touche pour revenir aux questions. Il n'en redit rien : 690 lignes contre
1 340, tout le reste vient du socle commun.
Ce que Proxmox a en propre est montré AVANT de lancer : le VMID, parce
que l'hôte ne dit « déjà pris » qu'après avoir téléchargé l'image, et
l'adresse qui s'en déduit sur un pont interne. Le stockage et le pont sont
lus sur l'hôte, jamais devinés. L'icône 🗄 distingue l'entrée du menu de
celle qui déploie ici même.
--- EN ---
Deploying on Proxmox meant twenty questions, one after another, never
seeing the whole plan. It now has the same screen as QEMU/KVM — catalogue
left, plan right, totals below — and a key to fall back to the questions.
It repeats none of it: 690 lines against 1,340, everything else comes
from the shared foundation.
What belongs to Proxmox is shown BEFORE launching: the VMID, because the
host only says "already taken" after downloading the image, and the
address derived from it on an internal bridge. Storage and bridge are read
from the host, never guessed. The 🗄 icon tells the menu entry apart from
the one that deploys right here.
Assisted-by: Claude Opus 5
2026-08-23 18:01:28 -04:00
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------- #
|
|
|
|
|
|
# Le plan
|
|
|
|
|
|
# ---------------------------------------------------------------- #
|
|
|
|
|
|
def _entries(self):
|
|
|
|
|
|
return catalog.get(self.arch) or []
|
|
|
|
|
|
|
|
|
|
|
|
def _selected_entries(self):
|
|
|
|
|
|
choisis = set(self.query_one("#f_catalog", SelectionList).selected)
|
|
|
|
|
|
return [e for e in self._entries() if entry_key(e) in choisis]
|
|
|
|
|
|
|
|
|
|
|
|
def _presets(self):
|
|
|
|
|
|
return {
|
|
|
|
|
|
"vcpus": ctx["cpu_presets"],
|
|
|
|
|
|
"ram": ctx["ram_presets"],
|
|
|
|
|
|
"disk": ctx["disk_presets"],
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
def _reload_catalog(self) -> None:
|
|
|
|
|
|
liste = self.query_one("#f_catalog", SelectionList)
|
|
|
|
|
|
garde = set(liste.selected)
|
|
|
|
|
|
liste.clear_options()
|
|
|
|
|
|
for e in self._entries():
|
|
|
|
|
|
cle = entry_key(e)
|
|
|
|
|
|
liste.add_option((entry_label(e), cle, cle in garde))
|
|
|
|
|
|
self._recompute()
|
|
|
|
|
|
self._mount_rows()
|
|
|
|
|
|
|
|
|
|
|
|
def _recompute(self) -> None:
|
|
|
|
|
|
entries = self._plan_entries()
|
|
|
|
|
|
self.vms = build_vms(
|
|
|
|
|
|
entries,
|
|
|
|
|
|
self.profile,
|
|
|
|
|
|
ctx["base_vcpus"],
|
|
|
|
|
|
ctx["host_cpu"],
|
|
|
|
|
|
self.custom,
|
|
|
|
|
|
self.overrides,
|
|
|
|
|
|
)
|
[FIX] qemu : ne pas poser ERPLibre sur une VM Proxmox VE
Choisir « Proxmox VE » comme système, c'est demander qu'il soit installé.
L'invite en ligne le savait ; le formulaire posait « ERPLibre + Odoo 18 »,
lui ajoutait les 5 Go réservés au dépôt et annonçait une cible make dans le
guide de la VM. La règle vit maintenant en un seul endroit, les deux
chemins la lisent, et un choix explicite l'emporte toujours sur elle.
Trois défauts trouvés derrière. Une commande par VM n'était retenue que si
DEUX VM différaient : déployée seule, la VM Proxmox retombait sur la
commande commune. Le disque choisi à la main disparaissait quand rien
n'était installé — 60 G demandés, 20 G créés. Et une taille absente des
préréglages marquait la rangée ✎ dès son montage.
--- EN ---
Choosing "Proxmox VE" as the system means asking for it to be installed.
The command-line prompt knew that; the form set "ERPLibre + Odoo 18", added
the 5 GB meant for the repository and advertised a make target in the VM's
guide. The rule now lives in one place, both paths read it, and an explicit
choice always wins over it.
Three defects behind it. A per-VM command was only used when TWO VMs
differed: deployed alone, the Proxmox VM fell back to the common command.
A hand-picked disk size vanished when nothing was installed — 60 G asked,
20 G created. And a size absent from the presets marked its row ✎ on mount.
Assisted-by: Claude Opus 5
2026-08-24 01:34:10 -04:00
|
|
|
|
# Ce qu'un système IMPOSE d'installer, posé sur le MODÈLE : le
|
|
|
|
|
|
# déploiement lit « install_cmd » VM par VM.
|
|
|
|
|
|
for vm in self.vms:
|
|
|
|
|
|
impose = distro_profiles.get(vm["distro"])
|
|
|
|
|
|
if impose and not vm.get("install_cmd"):
|
|
|
|
|
|
vm["install_label"], vm["install_cmd"] = impose
|
[ADD] proxmox : un écran pour déployer sur un hôte distant
Le déploiement Proxmox se faisait par vingt questions, l'une après
l'autre, sans jamais voir le plan entier. Il a maintenant le même écran
que QEMU/KVM — catalogue à gauche, plan à droite, totaux dessous — et une
touche pour revenir aux questions. Il n'en redit rien : 690 lignes contre
1 340, tout le reste vient du socle commun.
Ce que Proxmox a en propre est montré AVANT de lancer : le VMID, parce
que l'hôte ne dit « déjà pris » qu'après avoir téléchargé l'image, et
l'adresse qui s'en déduit sur un pont interne. Le stockage et le pont sont
lus sur l'hôte, jamais devinés. L'icône 🗄 distingue l'entrée du menu de
celle qui déploie ici même.
--- EN ---
Deploying on Proxmox meant twenty questions, one after another, never
seeing the whole plan. It now has the same screen as QEMU/KVM — catalogue
left, plan right, totals below — and a key to fall back to the questions.
It repeats none of it: 690 lines against 1,340, everything else comes
from the shared foundation.
What belongs to Proxmox is shown BEFORE launching: the VMID, because the
host only says "already taken" after downloading the image, and the
address derived from it on an internal bridge. Storage and bridge are read
from the host, never guessed. The 🗄 icon tells the menu entry apart from
the one that deploys right here.
Assisted-by: Claude Opus 5
2026-08-23 18:01:28 -04:00
|
|
|
|
self.rows = plan_rows(
|
[FIX] qemu : ne pas poser ERPLibre sur une VM Proxmox VE
Choisir « Proxmox VE » comme système, c'est demander qu'il soit installé.
L'invite en ligne le savait ; le formulaire posait « ERPLibre + Odoo 18 »,
lui ajoutait les 5 Go réservés au dépôt et annonçait une cible make dans le
guide de la VM. La règle vit maintenant en un seul endroit, les deux
chemins la lisent, et un choix explicite l'emporte toujours sur elle.
Trois défauts trouvés derrière. Une commande par VM n'était retenue que si
DEUX VM différaient : déployée seule, la VM Proxmox retombait sur la
commande commune. Le disque choisi à la main disparaissait quand rien
n'était installé — 60 G demandés, 20 G créés. Et une taille absente des
préréglages marquait la rangée ✎ dès son montage.
--- EN ---
Choosing "Proxmox VE" as the system means asking for it to be installed.
The command-line prompt knew that; the form set "ERPLibre + Odoo 18", added
the 5 GB meant for the repository and advertised a make target in the VM's
guide. The rule now lives in one place, both paths read it, and an explicit
choice always wins over it.
Three defects behind it. A per-VM command was only used when TWO VMs
differed: deployed alone, the Proxmox VM fell back to the common command.
A hand-picked disk size vanished when nothing was installed — 60 G asked,
20 G created. And a size absent from the presets marked its row ✎ on mount.
Assisted-by: Claude Opus 5
2026-08-24 01:34:10 -04:00
|
|
|
|
self.vms, noms_pris, orphelin=lambda _n: False
|
[ADD] proxmox : un écran pour déployer sur un hôte distant
Le déploiement Proxmox se faisait par vingt questions, l'une après
l'autre, sans jamais voir le plan entier. Il a maintenant le même écran
que QEMU/KVM — catalogue à gauche, plan à droite, totaux dessous — et une
touche pour revenir aux questions. Il n'en redit rien : 690 lignes contre
1 340, tout le reste vient du socle commun.
Ce que Proxmox a en propre est montré AVANT de lancer : le VMID, parce
que l'hôte ne dit « déjà pris » qu'après avoir téléchargé l'image, et
l'adresse qui s'en déduit sur un pont interne. Le stockage et le pont sont
lus sur l'hôte, jamais devinés. L'icône 🗄 distingue l'entrée du menu de
celle qui déploie ici même.
--- EN ---
Deploying on Proxmox meant twenty questions, one after another, never
seeing the whole plan. It now has the same screen as QEMU/KVM — catalogue
left, plan right, totals below — and a key to fall back to the questions.
It repeats none of it: 690 lines against 1,340, everything else comes
from the shared foundation.
What belongs to Proxmox is shown BEFORE launching: the VMID, because the
host only says "already taken" after downloading the image, and the
address derived from it on an internal bridge. Storage and bridge are read
from the host, never guessed. The 🗄 icon tells the menu entry apart from
the one that deploys right here.
Assisted-by: Claude Opus 5
2026-08-23 18:01:28 -04:00
|
|
|
|
)
|
[FIX] qemu : ne pas poser ERPLibre sur une VM Proxmox VE
Choisir « Proxmox VE » comme système, c'est demander qu'il soit installé.
L'invite en ligne le savait ; le formulaire posait « ERPLibre + Odoo 18 »,
lui ajoutait les 5 Go réservés au dépôt et annonçait une cible make dans le
guide de la VM. La règle vit maintenant en un seul endroit, les deux
chemins la lisent, et un choix explicite l'emporte toujours sur elle.
Trois défauts trouvés derrière. Une commande par VM n'était retenue que si
DEUX VM différaient : déployée seule, la VM Proxmox retombait sur la
commande commune. Le disque choisi à la main disparaissait quand rien
n'était installé — 60 G demandés, 20 G créés. Et une taille absente des
préréglages marquait la rangée ✎ dès son montage.
--- EN ---
Choosing "Proxmox VE" as the system means asking for it to be installed.
The command-line prompt knew that; the form set "ERPLibre + Odoo 18", added
the 5 GB meant for the repository and advertised a make target in the VM's
guide. The rule now lives in one place, both paths read it, and an explicit
choice always wins over it.
Three defects behind it. A per-VM command was only used when TWO VMs
differed: deployed alone, the Proxmox VM fell back to the common command.
A hand-picked disk size vanished when nothing was installed — 60 G asked,
20 G created. And a size absent from the presets marked its row ✎ on mount.
Assisted-by: Claude Opus 5
2026-08-24 01:34:10 -04:00
|
|
|
|
# Le supplément d'ERPLibre ne vaut que pour les VM qui l'auront
|
|
|
|
|
|
# vraiment : une VM Proxmox ne clonera pas le dépôt.
|
|
|
|
|
|
if self.query_one("#f_install", Checkbox).value:
|
|
|
|
|
|
commun = (self._install() or {}).get("cmd") or ""
|
|
|
|
|
|
for row in self.rows:
|
|
|
|
|
|
cmd_vm = row["vm"].get("install_cmd") or commun
|
|
|
|
|
|
if cmd_vm.strip() not in no_erplibre:
|
|
|
|
|
|
row["disk_gb"] += ctx.get("extra_disk_gb", 0)
|
[ADD] proxmox : un écran pour déployer sur un hôte distant
Le déploiement Proxmox se faisait par vingt questions, l'une après
l'autre, sans jamais voir le plan entier. Il a maintenant le même écran
que QEMU/KVM — catalogue à gauche, plan à droite, totaux dessous — et une
touche pour revenir aux questions. Il n'en redit rien : 690 lignes contre
1 340, tout le reste vient du socle commun.
Ce que Proxmox a en propre est montré AVANT de lancer : le VMID, parce
que l'hôte ne dit « déjà pris » qu'après avoir téléchargé l'image, et
l'adresse qui s'en déduit sur un pont interne. Le stockage et le pont sont
lus sur l'hôte, jamais devinés. L'icône 🗄 distingue l'entrée du menu de
celle qui déploie ici même.
--- EN ---
Deploying on Proxmox meant twenty questions, one after another, never
seeing the whole plan. It now has the same screen as QEMU/KVM — catalogue
left, plan right, totals below — and a key to fall back to the questions.
It repeats none of it: 690 lines against 1,340, everything else comes
from the shared foundation.
What belongs to Proxmox is shown BEFORE launching: the VMID, because the
host only says "already taken" after downloading the image, and the
address derived from it on an internal bridge. Storage and bridge are read
from the host, never guessed. The 🗄 icon tells the menu entry apart from
the one that deploys right here.
Assisted-by: Claude Opus 5
2026-08-23 18:01:28 -04:00
|
|
|
|
for row, entry in zip(self.rows, entries):
|
|
|
|
|
|
cle = entry_key(entry)
|
|
|
|
|
|
row["custom"] = bool(self.overrides.get(cle))
|
|
|
|
|
|
row["locked"] = cle in self.locked
|
|
|
|
|
|
assign_vmids(
|
|
|
|
|
|
self.rows,
|
|
|
|
|
|
vmids_pris,
|
|
|
|
|
|
self._vmid_start(),
|
|
|
|
|
|
lambda vmid: (ctx.get("ipconfig") or (lambda _v: "ip=dhcp"))(
|
|
|
|
|
|
self._bridge(), vmid
|
|
|
|
|
|
),
|
|
|
|
|
|
)
|
|
|
|
|
|
self._render_plan()
|
|
|
|
|
|
|
|
|
|
|
|
def _vmid_start(self):
|
|
|
|
|
|
brut = self.query_one("#f_vmid", Input).value.strip()
|
|
|
|
|
|
return (
|
|
|
|
|
|
int(brut) if brut.isdigit() else (ctx.get("next_vmid") or 100)
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def _bridge(self):
|
|
|
|
|
|
valeur = self.query_one("#f_bridge", Select).value
|
[ADD] proxmox : créer le pont manquant depuis l'écran
Sans pont, « qm create » est impossible — et l'écran refusait de déployer
« aucun pont sur l'hôte » sans offrir le moindre moyen d'en avoir un. Une
Proxmox installée SUR Debian n'en a jamais : l'ISO en crée un, pas la
procédure sur Debian.
Deux moments, donc. Avant l'écran, la question se pose dans le terminal, où
l'on peut expliquer les deux voies et montrer ce qui s'exécute. Dans l'écran,
le sélecteur porte « ➕ créer un pont interne vmbr0 (10.10.10.1/24) + NAT » :
la création part dans un fil, l'affichage reste vivant, et le pont créé se
sélectionne tout seul. Elle ne demande rien parce qu'un pont interne ne
touche à aucune interface physique ; un pont sur le LAN déplace l'adresse de
l'hôte et coupe la session, donc il reste manuel.
--- EN ---
With no bridge, "qm create" is impossible — and the screen refused to deploy
"no bridge on the host" without offering any way to get one. A Proxmox
installed ON Debian never has one: the ISO creates it, the Debian procedure
does not.
Two moments, then. Before the screen, the question is asked in the terminal,
where both ways can be explained and the commands shown. In the screen, the
selector carries "➕ create an internal vmbr0 (10.10.10.1/24) + NAT": creation
runs in a thread, the display stays alive, and the new bridge selects itself.
It asks nothing because an internal bridge touches no physical NIC; a bridge
on the LAN moves the host's address and cuts the session, so it stays manual.
Assisted-by: Claude Opus 5
2026-08-24 04:59:13 -04:00
|
|
|
|
if valeur is SELECT_NULL or valeur == CREER_PONT:
|
|
|
|
|
|
# La sentinelle n'est pas un pont : la rendre ferait déployer
|
|
|
|
|
|
# une VM sur « __creer_pont__ ».
|
|
|
|
|
|
return ""
|
|
|
|
|
|
return valeur
|
[ADD] proxmox : un écran pour déployer sur un hôte distant
Le déploiement Proxmox se faisait par vingt questions, l'une après
l'autre, sans jamais voir le plan entier. Il a maintenant le même écran
que QEMU/KVM — catalogue à gauche, plan à droite, totaux dessous — et une
touche pour revenir aux questions. Il n'en redit rien : 690 lignes contre
1 340, tout le reste vient du socle commun.
Ce que Proxmox a en propre est montré AVANT de lancer : le VMID, parce
que l'hôte ne dit « déjà pris » qu'après avoir téléchargé l'image, et
l'adresse qui s'en déduit sur un pont interne. Le stockage et le pont sont
lus sur l'hôte, jamais devinés. L'icône 🗄 distingue l'entrée du menu de
celle qui déploie ici même.
--- EN ---
Deploying on Proxmox meant twenty questions, one after another, never
seeing the whole plan. It now has the same screen as QEMU/KVM — catalogue
left, plan right, totals below — and a key to fall back to the questions.
It repeats none of it: 690 lines against 1,340, everything else comes
from the shared foundation.
What belongs to Proxmox is shown BEFORE launching: the VMID, because the
host only says "already taken" after downloading the image, and the
address derived from it on an internal bridge. Storage and bridge are read
from the host, never guessed. The 🗄 icon tells the menu entry apart from
the one that deploys right here.
Assisted-by: Claude Opus 5
2026-08-23 18:01:28 -04:00
|
|
|
|
|
|
|
|
|
|
def _storage(self):
|
|
|
|
|
|
valeur = self.query_one("#f_storage", Select).value
|
|
|
|
|
|
return "" if valeur is SELECT_NULL else valeur
|
|
|
|
|
|
|
|
|
|
|
|
def _row_head(self, index, row):
|
|
|
|
|
|
"""La ligne de titre du socle, plus ce que Proxmox ajoute : le
|
|
|
|
|
|
VMID et l'adresse. Les deux sont décidés ICI et pas par l'hôte —
|
|
|
|
|
|
les montrer avant de lancer est le seul moyen de les vérifier."""
|
|
|
|
|
|
base = PlanMixin._row_head(self, index, row)
|
|
|
|
|
|
vm = row["vm"]
|
|
|
|
|
|
if row["state"] == "exists":
|
|
|
|
|
|
return base
|
|
|
|
|
|
adresse = (vm.get("ipconfig") or "").replace("ip=", "")
|
|
|
|
|
|
return f"{base} VMID {vm.get('vmid', '?')} {adresse}"
|
|
|
|
|
|
|
|
|
|
|
|
def _mount_rows(self) -> None:
|
|
|
|
|
|
"""(Re)construit le panneau droit.
|
|
|
|
|
|
|
|
|
|
|
|
Le verrou couvre TOUT le montage : poser « value= » sur un Select
|
|
|
|
|
|
fait émettre un Changed à Textual, que on_select_changed prendrait
|
|
|
|
|
|
pour une saisie."""
|
|
|
|
|
|
self._syncing = True
|
|
|
|
|
|
self._gen += 1
|
|
|
|
|
|
plan = self.query_one("#plan", VerticalScroll)
|
|
|
|
|
|
plan.remove_children()
|
|
|
|
|
|
cartes = []
|
|
|
|
|
|
for i, r in enumerate(self.rows):
|
|
|
|
|
|
vm = r["vm"]
|
|
|
|
|
|
cle = self._row_key(i)
|
|
|
|
|
|
item = self._plan_entries()[i]
|
|
|
|
|
|
rangee = Horizontal(
|
|
|
|
|
|
Button("+", id=f"p{i}", classes="vmcopy"),
|
|
|
|
|
|
Button("✎", id=f"r{i}", classes="vmcopy"),
|
|
|
|
|
|
Button(
|
|
|
|
|
|
"🔒" if cle in self.locked else "🔓",
|
|
|
|
|
|
id=f"l{i}",
|
|
|
|
|
|
variant=(
|
|
|
|
|
|
"success" if cle in self.locked else "default"
|
|
|
|
|
|
),
|
|
|
|
|
|
classes="vmlock",
|
|
|
|
|
|
),
|
|
|
|
|
|
*res_row_widgets(
|
|
|
|
|
|
i,
|
|
|
|
|
|
vm,
|
|
|
|
|
|
self._presets(),
|
|
|
|
|
|
labels={"vcpus": t("vCPU")},
|
|
|
|
|
|
null=SELECT_NULL,
|
|
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
Button("−", id=f"m{i}", classes="vmcopy")
|
|
|
|
|
|
if item.get("instance")
|
|
|
|
|
|
else Static("", classes="vmcopy")
|
|
|
|
|
|
),
|
|
|
|
|
|
classes="vmrow",
|
|
|
|
|
|
)
|
|
|
|
|
|
cartes.append(
|
|
|
|
|
|
Vertical(
|
|
|
|
|
|
Static(self._row_head(i, r), id=f"h{i}"),
|
|
|
|
|
|
rangee,
|
|
|
|
|
|
classes=(
|
|
|
|
|
|
"vmcard locked" if cle in self.locked else "vmcard"
|
|
|
|
|
|
),
|
|
|
|
|
|
)
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
# Marque de génération sur CHAQUE widget : « walk_children() » ne
|
|
|
|
|
|
# voit rien avant le montage, les enfants attendent dans
|
|
|
|
|
|
# « _pending_children ».
|
|
|
|
|
|
def marquer(node):
|
|
|
|
|
|
node._el_gen = self._gen
|
|
|
|
|
|
for child in getattr(node, "_pending_children", None) or []:
|
|
|
|
|
|
marquer(child)
|
|
|
|
|
|
|
|
|
|
|
|
for carte in cartes:
|
|
|
|
|
|
marquer(carte)
|
|
|
|
|
|
plan.mount_all(cartes)
|
|
|
|
|
|
self._shown_ids = self._row_ids()
|
|
|
|
|
|
self.call_after_refresh(self._after_mount_rows)
|
|
|
|
|
|
|
|
|
|
|
|
def _after_mount_rows(self) -> None:
|
|
|
|
|
|
self._sync_free_inputs()
|
|
|
|
|
|
self._syncing = False
|
|
|
|
|
|
|
|
|
|
|
|
def _render_plan(self) -> None:
|
|
|
|
|
|
for i, r in enumerate(self.rows):
|
|
|
|
|
|
try:
|
|
|
|
|
|
self.query_one(f"#h{i}", Static).update(
|
|
|
|
|
|
self._row_head(i, r)
|
|
|
|
|
|
)
|
|
|
|
|
|
except Exception:
|
|
|
|
|
|
pass
|
|
|
|
|
|
n, cpu, ram, disque = plan_totals(self.rows)
|
|
|
|
|
|
libre = ctx.get("free_ram") or 0
|
[ADD] todo : dire la place qui reste sous le plan de déploiement
La ligne de totaux annonçait « ~126 G » sans dire sur quoi : la demande
seule ne dit pas si ça rentre, et on l'apprenait au déploiement. Elle dit
maintenant la demande, ce qui reste et la capacité — « ~126 G / 20 G libres
sur 270 G » — et prévient dès que le plan dépasse. Les trois limites (RAM,
disque, cœurs) s'affichent ensemble : n'en montrer qu'une cachait les
autres. Sur Proxmox, la place vient du stockage choisi, que « pvesm status »
donnait déjà.
Deux défauts trouvés en le faisant : la marque de génération, exigée de tous
les widgets, faisait taire chaque réglage commun de l'écran Proxmox, et
« [x1] » disparaissait, lu comme une balise Rich.
--- EN ---
The totals line said "~126 G" without saying out of what: the demand alone
does not tell whether it fits, and one found out at deploy time. It now says
the demand, what is left and the capacity — "~126 G / 20 G free of 270 G" —
and warns as soon as the plan exceeds it. The three limits (RAM, disk,
cores) show together: showing only one hid the others. On Proxmox the room
comes from the chosen storage, which "pvesm status" already gave.
Two defects found on the way: the generation mark, required of every widget,
silenced each common setting on the Proxmox screen, and "[x1]" vanished,
read as a Rich tag.
Assisted-by: Claude Opus 5
2026-08-24 01:11:09 -04:00
|
|
|
|
# La place du stockage CHOISI : elle change avec la liste, donc
|
|
|
|
|
|
# elle se relit à chaque rendu plutôt qu'une fois au montage.
|
|
|
|
|
|
place = gib((ctx.get("storage_avail") or {}).get(self._storage()))
|
|
|
|
|
|
alertes = []
|
|
|
|
|
|
if libre and ram > libre:
|
|
|
|
|
|
alertes.append(t("more RAM than the host has free"))
|
|
|
|
|
|
if place and disque > place:
|
|
|
|
|
|
alertes.append(t("more disk than the storage has free"))
|
|
|
|
|
|
alerte = f" ⚠ {' · '.join(alertes)}" if alertes else ""
|
[ADD] proxmox : un écran pour déployer sur un hôte distant
Le déploiement Proxmox se faisait par vingt questions, l'une après
l'autre, sans jamais voir le plan entier. Il a maintenant le même écran
que QEMU/KVM — catalogue à gauche, plan à droite, totaux dessous — et une
touche pour revenir aux questions. Il n'en redit rien : 690 lignes contre
1 340, tout le reste vient du socle commun.
Ce que Proxmox a en propre est montré AVANT de lancer : le VMID, parce
que l'hôte ne dit « déjà pris » qu'après avoir téléchargé l'image, et
l'adresse qui s'en déduit sur un pont interne. Le stockage et le pont sont
lus sur l'hôte, jamais devinés. L'icône 🗄 distingue l'entrée du menu de
celle qui déploie ici même.
--- EN ---
Deploying on Proxmox meant twenty questions, one after another, never
seeing the whole plan. It now has the same screen as QEMU/KVM — catalogue
left, plan right, totals below — and a key to fall back to the questions.
It repeats none of it: 690 lines against 1,340, everything else comes
from the shared foundation.
What belongs to Proxmox is shown BEFORE launching: the VMID, because the
host only says "already taken" after downloading the image, and the
address derived from it on an internal bridge. Storage and bridge are read
from the host, never guessed. The 🗄 icon tells the menu entry apart from
the one that deploys right here.
Assisted-by: Claude Opus 5
2026-08-23 18:01:28 -04:00
|
|
|
|
self.query_one("#totals", Static).update(
|
|
|
|
|
|
f" {n} {t('VM')} {cpu} vCPU {ram} Mo RAM "
|
[ADD] todo : dire la place qui reste sous le plan de déploiement
La ligne de totaux annonçait « ~126 G » sans dire sur quoi : la demande
seule ne dit pas si ça rentre, et on l'apprenait au déploiement. Elle dit
maintenant la demande, ce qui reste et la capacité — « ~126 G / 20 G libres
sur 270 G » — et prévient dès que le plan dépasse. Les trois limites (RAM,
disque, cœurs) s'affichent ensemble : n'en montrer qu'une cachait les
autres. Sur Proxmox, la place vient du stockage choisi, que « pvesm status »
donnait déjà.
Deux défauts trouvés en le faisant : la marque de génération, exigée de tous
les widgets, faisait taire chaque réglage commun de l'écran Proxmox, et
« [x1] » disparaissait, lu comme une balise Rich.
--- EN ---
The totals line said "~126 G" without saying out of what: the demand alone
does not tell whether it fits, and one found out at deploy time. It now says
the demand, what is left and the capacity — "~126 G / 20 G free of 270 G" —
and warns as soon as the plan exceeds it. The three limits (RAM, disk,
cores) show together: showing only one hid the others. On Proxmox the room
comes from the chosen storage, which "pvesm status" already gave.
Two defects found on the way: the generation mark, required of every widget,
silenced each common setting on the Proxmox screen, and "[x1]" vanished,
read as a Rich tag.
Assisted-by: Claude Opus 5
2026-08-24 01:11:09 -04:00
|
|
|
|
f"{disk_note(disque, place)} {res_label(self.profile)}"
|
[ADD] proxmox : un écran pour déployer sur un hôte distant
Le déploiement Proxmox se faisait par vingt questions, l'une après
l'autre, sans jamais voir le plan entier. Il a maintenant le même écran
que QEMU/KVM — catalogue à gauche, plan à droite, totaux dessous — et une
touche pour revenir aux questions. Il n'en redit rien : 690 lignes contre
1 340, tout le reste vient du socle commun.
Ce que Proxmox a en propre est montré AVANT de lancer : le VMID, parce
que l'hôte ne dit « déjà pris » qu'après avoir téléchargé l'image, et
l'adresse qui s'en déduit sur un pont interne. Le stockage et le pont sont
lus sur l'hôte, jamais devinés. L'icône 🗄 distingue l'entrée du menu de
celle qui déploie ici même.
--- EN ---
Deploying on Proxmox meant twenty questions, one after another, never
seeing the whole plan. It now has the same screen as QEMU/KVM — catalogue
left, plan right, totals below — and a key to fall back to the questions.
It repeats none of it: 690 lines against 1,340, everything else comes
from the shared foundation.
What belongs to Proxmox is shown BEFORE launching: the VMID, because the
host only says "already taken" after downloading the image, and the
address derived from it on an internal bridge. Storage and bridge are read
from the host, never guessed. The 🗄 icon tells the menu entry apart from
the one that deploys right here.
Assisted-by: Claude Opus 5
2026-08-23 18:01:28 -04:00
|
|
|
|
f" {t('storage')} {self._storage() or '?'}"
|
|
|
|
|
|
f" {t('bridge')} {self._bridge() or '?'}{alerte}"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def _refresh_after(self, remonter=False) -> None:
|
|
|
|
|
|
"""Recalcule, et ne remonte les rangées que si le JEU a changé :
|
|
|
|
|
|
un remontage à chaque frappe volerait le focus."""
|
|
|
|
|
|
self._recompute()
|
|
|
|
|
|
if remonter or self._row_ids() != self._shown_ids:
|
|
|
|
|
|
self._mount_rows()
|
|
|
|
|
|
else:
|
|
|
|
|
|
self._sync_free_inputs()
|
|
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------- #
|
|
|
|
|
|
# Les messages
|
|
|
|
|
|
# ---------------------------------------------------------------- #
|
|
|
|
|
|
def on_selection_list_selected_changed(self, _event) -> None:
|
|
|
|
|
|
self._refresh_after()
|
|
|
|
|
|
|
|
|
|
|
|
def on_radio_set_changed(self, event) -> None:
|
|
|
|
|
|
if event.radio_set.id == "f_arch":
|
|
|
|
|
|
self.arch = arches[event.index]
|
|
|
|
|
|
self._reload_catalog()
|
|
|
|
|
|
return
|
|
|
|
|
|
if event.radio_set.id == "f_profile":
|
|
|
|
|
|
choix = ("1", "2", "3", "4", "custom")[event.index]
|
|
|
|
|
|
self.profile = choix
|
|
|
|
|
|
sur_mesure = choix == "custom"
|
|
|
|
|
|
for champ in RES_FIELDS:
|
|
|
|
|
|
self.query_one(RES_FIELDS[champ][0], Select).disabled = (
|
|
|
|
|
|
not sur_mesure
|
|
|
|
|
|
)
|
|
|
|
|
|
if not sur_mesure:
|
|
|
|
|
|
self._show_free(champ, False)
|
|
|
|
|
|
# Un réglage commun reprend la main sur les VM non figées :
|
|
|
|
|
|
# c'est le sens même du mot « commun ».
|
|
|
|
|
|
self._clear_overrides(tuple(RES_FIELDS))
|
|
|
|
|
|
self._refresh_after(remonter=True)
|
|
|
|
|
|
|
[UPD] todo : nommer la case d'installation par ce qu'elle commande
« Installer ERPLibre » commandait TOUTE installation, l'hyperviseur Proxmox
VE compris : décochée, une VM Proxmox restait une Debian nue sans que rien
ne l'explique. Elle devient « Installer un logiciel dans la VM », passe sous
le type de VM — juste avant les sections qu'elle commande — et le suivi la
quitte, puisqu'il regarde la VM arriver même quand rien ne s'installe.
Ce qu'elle rend sans effet se grise, titre compris. Trois états et non deux :
sans installation mais AVEC un bureau, le magasin d'applications et les
outils de la phase « avant » servent encore ; seuls ceux qui vivent dans le
dépôt s'éteignent. Griser en bloc aurait menti autant que de tout laisser.
--- EN ---
"Install ERPLibre" commanded EVERY install, the Proxmox VE hypervisor
included: unticked, a Proxmox VM stayed a bare Debian with nothing to explain
it. It becomes "Install software in the VM", moves under the VM type — right
before the sections it commands — and the dashboard leaves it, since that
watches the VM arrive even when nothing is installed.
What it makes ineffective now greys out, title included. Three states, not
two: with no install but WITH a desktop, the application store and the
"before" tools still act; only those living in the repository go dark.
Greying the lot would have lied as much as greying nothing.
Assisted-by: Claude Opus 5
2026-08-24 02:32:09 -04:00
|
|
|
|
def _sync_install_deps(self) -> None:
|
|
|
|
|
|
"""Grise ce que la case rend sans effet : la branche et le profil.
|
|
|
|
|
|
|
|
|
|
|
|
Le suivi n'en fait pas partie — il regarde la VM arriver même
|
|
|
|
|
|
quand rien ne s'installe."""
|
|
|
|
|
|
installe = self.query_one("#f_install", Checkbox).value
|
|
|
|
|
|
for cible in ("#f_profile_install", "#f_branch"):
|
|
|
|
|
|
try:
|
|
|
|
|
|
self.query_one(cible).disabled = not installe
|
|
|
|
|
|
except Exception:
|
|
|
|
|
|
pass
|
|
|
|
|
|
try:
|
|
|
|
|
|
self.query_one("#t_install").set_class(not installe, "off")
|
|
|
|
|
|
except Exception:
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
[ADD] proxmox : un écran pour déployer sur un hôte distant
Le déploiement Proxmox se faisait par vingt questions, l'une après
l'autre, sans jamais voir le plan entier. Il a maintenant le même écran
que QEMU/KVM — catalogue à gauche, plan à droite, totaux dessous — et une
touche pour revenir aux questions. Il n'en redit rien : 690 lignes contre
1 340, tout le reste vient du socle commun.
Ce que Proxmox a en propre est montré AVANT de lancer : le VMID, parce
que l'hôte ne dit « déjà pris » qu'après avoir téléchargé l'image, et
l'adresse qui s'en déduit sur un pont interne. Le stockage et le pont sont
lus sur l'hôte, jamais devinés. L'icône 🗄 distingue l'entrée du menu de
celle qui déploie ici même.
--- EN ---
Deploying on Proxmox meant twenty questions, one after another, never
seeing the whole plan. It now has the same screen as QEMU/KVM — catalogue
left, plan right, totals below — and a key to fall back to the questions.
It repeats none of it: 690 lines against 1,340, everything else comes
from the shared foundation.
What belongs to Proxmox is shown BEFORE launching: the VMID, because the
host only says "already taken" after downloading the image, and the
address derived from it on an internal bridge. Storage and bridge are read
from the host, never guessed. The 🗄 icon tells the menu entry apart from
the one that deploys right here.
Assisted-by: Claude Opus 5
2026-08-23 18:01:28 -04:00
|
|
|
|
def on_checkbox_changed(self, event) -> None:
|
|
|
|
|
|
if event.checkbox.id == "f_install":
|
|
|
|
|
|
# Le disque d'ERPLibre entre — ou sort — du total.
|
[UPD] todo : nommer la case d'installation par ce qu'elle commande
« Installer ERPLibre » commandait TOUTE installation, l'hyperviseur Proxmox
VE compris : décochée, une VM Proxmox restait une Debian nue sans que rien
ne l'explique. Elle devient « Installer un logiciel dans la VM », passe sous
le type de VM — juste avant les sections qu'elle commande — et le suivi la
quitte, puisqu'il regarde la VM arriver même quand rien ne s'installe.
Ce qu'elle rend sans effet se grise, titre compris. Trois états et non deux :
sans installation mais AVEC un bureau, le magasin d'applications et les
outils de la phase « avant » servent encore ; seuls ceux qui vivent dans le
dépôt s'éteignent. Griser en bloc aurait menti autant que de tout laisser.
--- EN ---
"Install ERPLibre" commanded EVERY install, the Proxmox VE hypervisor
included: unticked, a Proxmox VM stayed a bare Debian with nothing to explain
it. It becomes "Install software in the VM", moves under the VM type — right
before the sections it commands — and the dashboard leaves it, since that
watches the VM arrive even when nothing is installed.
What it makes ineffective now greys out, title included. Three states, not
two: with no install but WITH a desktop, the application store and the
"before" tools still act; only those living in the repository go dark.
Greying the lot would have lied as much as greying nothing.
Assisted-by: Claude Opus 5
2026-08-24 02:32:09 -04:00
|
|
|
|
self._sync_install_deps()
|
[ADD] proxmox : un écran pour déployer sur un hôte distant
Le déploiement Proxmox se faisait par vingt questions, l'une après
l'autre, sans jamais voir le plan entier. Il a maintenant le même écran
que QEMU/KVM — catalogue à gauche, plan à droite, totaux dessous — et une
touche pour revenir aux questions. Il n'en redit rien : 690 lignes contre
1 340, tout le reste vient du socle commun.
Ce que Proxmox a en propre est montré AVANT de lancer : le VMID, parce
que l'hôte ne dit « déjà pris » qu'après avoir téléchargé l'image, et
l'adresse qui s'en déduit sur un pont interne. Le stockage et le pont sont
lus sur l'hôte, jamais devinés. L'icône 🗄 distingue l'entrée du menu de
celle qui déploie ici même.
--- EN ---
Deploying on Proxmox meant twenty questions, one after another, never
seeing the whole plan. It now has the same screen as QEMU/KVM — catalogue
left, plan right, totals below — and a key to fall back to the questions.
It repeats none of it: 690 lines against 1,340, everything else comes
from the shared foundation.
What belongs to Proxmox is shown BEFORE launching: the VMID, because the
host only says "already taken" after downloading the image, and the
address derived from it on an internal bridge. Storage and bridge are read
from the host, never guessed. The 🗄 icon tells the menu entry apart from
the one that deploys right here.
Assisted-by: Claude Opus 5
2026-08-23 18:01:28 -04:00
|
|
|
|
self._refresh_after()
|
|
|
|
|
|
|
|
|
|
|
|
def on_input_changed(self, event) -> None:
|
|
|
|
|
|
if event.input.id == "f_vmid":
|
|
|
|
|
|
self._refresh_after()
|
|
|
|
|
|
|
|
|
|
|
|
def on_input_submitted(self, event) -> None:
|
|
|
|
|
|
ident = event.input.id or ""
|
|
|
|
|
|
if ident in {RES_FIELDS[c][1][1:] for c in RES_FIELDS}:
|
|
|
|
|
|
self._apply_free(ident.split("_", 1)[1])
|
|
|
|
|
|
self._refresh_after(remonter=True)
|
|
|
|
|
|
return
|
|
|
|
|
|
if ident.startswith("c") and "_" in ident:
|
|
|
|
|
|
rang, champ = ident[1:].split("_", 1)
|
|
|
|
|
|
if rang.isdigit():
|
|
|
|
|
|
self._set_override(
|
|
|
|
|
|
int(rang), champ, self._read_row_free(int(rang), champ)
|
|
|
|
|
|
)
|
|
|
|
|
|
self._refresh_after()
|
|
|
|
|
|
|
|
|
|
|
|
def on_select_changed(self, event) -> None:
|
[ADD] todo : dire la place qui reste sous le plan de déploiement
La ligne de totaux annonçait « ~126 G » sans dire sur quoi : la demande
seule ne dit pas si ça rentre, et on l'apprenait au déploiement. Elle dit
maintenant la demande, ce qui reste et la capacité — « ~126 G / 20 G libres
sur 270 G » — et prévient dès que le plan dépasse. Les trois limites (RAM,
disque, cœurs) s'affichent ensemble : n'en montrer qu'une cachait les
autres. Sur Proxmox, la place vient du stockage choisi, que « pvesm status »
donnait déjà.
Deux défauts trouvés en le faisant : la marque de génération, exigée de tous
les widgets, faisait taire chaque réglage commun de l'écran Proxmox, et
« [x1] » disparaissait, lu comme une balise Rich.
--- EN ---
The totals line said "~126 G" without saying out of what: the demand alone
does not tell whether it fits, and one found out at deploy time. It now says
the demand, what is left and the capacity — "~126 G / 20 G free of 270 G" —
and warns as soon as the plan exceeds it. The three limits (RAM, disk,
cores) show together: showing only one hid the others. On Proxmox the room
comes from the chosen storage, which "pvesm status" already gave.
Two defects found on the way: the generation mark, required of every widget,
silenced each common setting on the Proxmox screen, and "[x1]" vanished,
read as a Rich tag.
Assisted-by: Claude Opus 5
2026-08-24 01:11:09 -04:00
|
|
|
|
if self._syncing:
|
[ADD] proxmox : un écran pour déployer sur un hôte distant
Le déploiement Proxmox se faisait par vingt questions, l'une après
l'autre, sans jamais voir le plan entier. Il a maintenant le même écran
que QEMU/KVM — catalogue à gauche, plan à droite, totaux dessous — et une
touche pour revenir aux questions. Il n'en redit rien : 690 lignes contre
1 340, tout le reste vient du socle commun.
Ce que Proxmox a en propre est montré AVANT de lancer : le VMID, parce
que l'hôte ne dit « déjà pris » qu'après avoir téléchargé l'image, et
l'adresse qui s'en déduit sur un pont interne. Le stockage et le pont sont
lus sur l'hôte, jamais devinés. L'icône 🗄 distingue l'entrée du menu de
celle qui déploie ici même.
--- EN ---
Deploying on Proxmox meant twenty questions, one after another, never
seeing the whole plan. It now has the same screen as QEMU/KVM — catalogue
left, plan right, totals below — and a key to fall back to the questions.
It repeats none of it: 690 lines against 1,340, everything else comes
from the shared foundation.
What belongs to Proxmox is shown BEFORE launching: the VMID, because the
host only says "already taken" after downloading the image, and the
address derived from it on an internal bridge. Storage and bridge are read
from the host, never guessed. The 🗄 icon tells the menu entry apart from
the one that deploys right here.
Assisted-by: Claude Opus 5
2026-08-23 18:01:28 -04:00
|
|
|
|
return
|
|
|
|
|
|
ident = event.select.id or ""
|
[ADD] todo : dire la place qui reste sous le plan de déploiement
La ligne de totaux annonçait « ~126 G » sans dire sur quoi : la demande
seule ne dit pas si ça rentre, et on l'apprenait au déploiement. Elle dit
maintenant la demande, ce qui reste et la capacité — « ~126 G / 20 G libres
sur 270 G » — et prévient dès que le plan dépasse. Les trois limites (RAM,
disque, cœurs) s'affichent ensemble : n'en montrer qu'une cachait les
autres. Sur Proxmox, la place vient du stockage choisi, que « pvesm status »
donnait déjà.
Deux défauts trouvés en le faisant : la marque de génération, exigée de tous
les widgets, faisait taire chaque réglage commun de l'écran Proxmox, et
« [x1] » disparaissait, lu comme une balise Rich.
--- EN ---
The totals line said "~126 G" without saying out of what: the demand alone
does not tell whether it fits, and one found out at deploy time. It now says
the demand, what is left and the capacity — "~126 G / 20 G free of 270 G" —
and warns as soon as the plan exceeds it. The three limits (RAM, disk,
cores) show together: showing only one hid the others. On Proxmox the room
comes from the chosen storage, which "pvesm status" already gave.
Two defects found on the way: the generation mark, required of every widget,
silenced each common setting on the Proxmox screen, and "[x1]" vanished,
read as a Rich tag.
Assisted-by: Claude Opus 5
2026-08-24 01:11:09 -04:00
|
|
|
|
# La marque de génération ne concerne QUE les widgets de rangée :
|
|
|
|
|
|
# un widget global n'en porte pas. L'exiger de tous revenait à
|
|
|
|
|
|
# ignorer chaque réglage commun — mesuré, ni le stockage, ni la
|
|
|
|
|
|
# RAM générale n'atteignaient le plan.
|
[FIX] qemu : ne pas poser ERPLibre sur une VM Proxmox VE
Choisir « Proxmox VE » comme système, c'est demander qu'il soit installé.
L'invite en ligne le savait ; le formulaire posait « ERPLibre + Odoo 18 »,
lui ajoutait les 5 Go réservés au dépôt et annonçait une cible make dans le
guide de la VM. La règle vit maintenant en un seul endroit, les deux
chemins la lisent, et un choix explicite l'emporte toujours sur elle.
Trois défauts trouvés derrière. Une commande par VM n'était retenue que si
DEUX VM différaient : déployée seule, la VM Proxmox retombait sur la
commande commune. Le disque choisi à la main disparaissait quand rien
n'était installé — 60 G demandés, 20 G créés. Et une taille absente des
préréglages marquait la rangée ✎ dès son montage.
--- EN ---
Choosing "Proxmox VE" as the system means asking for it to be installed.
The command-line prompt knew that; the form set "ERPLibre + Odoo 18", added
the 5 GB meant for the repository and advertised a make target in the VM's
guide. The rule now lives in one place, both paths read it, and an explicit
choice always wins over it.
Three defects behind it. A per-VM command was only used when TWO VMs
differed: deployed alone, the Proxmox VM fell back to the common command.
A hand-picked disk size vanished when nothing was installed — 60 G asked,
20 G created. And a size absent from the presets marked its row ✎ on mount.
Assisted-by: Claude Opus 5
2026-08-24 01:34:10 -04:00
|
|
|
|
if re.match(r"v\d+_", ident) and not self._is_current(
|
|
|
|
|
|
event.select
|
|
|
|
|
|
):
|
[ADD] todo : dire la place qui reste sous le plan de déploiement
La ligne de totaux annonçait « ~126 G » sans dire sur quoi : la demande
seule ne dit pas si ça rentre, et on l'apprenait au déploiement. Elle dit
maintenant la demande, ce qui reste et la capacité — « ~126 G / 20 G libres
sur 270 G » — et prévient dès que le plan dépasse. Les trois limites (RAM,
disque, cœurs) s'affichent ensemble : n'en montrer qu'une cachait les
autres. Sur Proxmox, la place vient du stockage choisi, que « pvesm status »
donnait déjà.
Deux défauts trouvés en le faisant : la marque de génération, exigée de tous
les widgets, faisait taire chaque réglage commun de l'écran Proxmox, et
« [x1] » disparaissait, lu comme une balise Rich.
--- EN ---
The totals line said "~126 G" without saying out of what: the demand alone
does not tell whether it fits, and one found out at deploy time. It now says
the demand, what is left and the capacity — "~126 G / 20 G free of 270 G" —
and warns as soon as the plan exceeds it. The three limits (RAM, disk,
cores) show together: showing only one hid the others. On Proxmox the room
comes from the chosen storage, which "pvesm status" already gave.
Two defects found on the way: the generation mark, required of every widget,
silenced each common setting on the Proxmox screen, and "[x1]" vanished,
read as a Rich tag.
Assisted-by: Claude Opus 5
2026-08-24 01:11:09 -04:00
|
|
|
|
return
|
[ADD] proxmox : créer le pont manquant depuis l'écran
Sans pont, « qm create » est impossible — et l'écran refusait de déployer
« aucun pont sur l'hôte » sans offrir le moindre moyen d'en avoir un. Une
Proxmox installée SUR Debian n'en a jamais : l'ISO en crée un, pas la
procédure sur Debian.
Deux moments, donc. Avant l'écran, la question se pose dans le terminal, où
l'on peut expliquer les deux voies et montrer ce qui s'exécute. Dans l'écran,
le sélecteur porte « ➕ créer un pont interne vmbr0 (10.10.10.1/24) + NAT » :
la création part dans un fil, l'affichage reste vivant, et le pont créé se
sélectionne tout seul. Elle ne demande rien parce qu'un pont interne ne
touche à aucune interface physique ; un pont sur le LAN déplace l'adresse de
l'hôte et coupe la session, donc il reste manuel.
--- EN ---
With no bridge, "qm create" is impossible — and the screen refused to deploy
"no bridge on the host" without offering any way to get one. A Proxmox
installed ON Debian never has one: the ISO creates it, the Debian procedure
does not.
Two moments, then. Before the screen, the question is asked in the terminal,
where both ways can be explained and the commands shown. In the screen, the
selector carries "➕ create an internal vmbr0 (10.10.10.1/24) + NAT": creation
runs in a thread, the display stays alive, and the new bridge selects itself.
It asks nothing because an internal bridge touches no physical NIC; a bridge
on the LAN moves the host's address and cuts the session, so it stays manual.
Assisted-by: Claude Opus 5
2026-08-24 04:59:13 -04:00
|
|
|
|
if ident == "f_bridge" and event.value == CREER_PONT:
|
|
|
|
|
|
self._creer_pont()
|
|
|
|
|
|
return
|
[ADD] proxmox : un écran pour déployer sur un hôte distant
Le déploiement Proxmox se faisait par vingt questions, l'une après
l'autre, sans jamais voir le plan entier. Il a maintenant le même écran
que QEMU/KVM — catalogue à gauche, plan à droite, totaux dessous — et une
touche pour revenir aux questions. Il n'en redit rien : 690 lignes contre
1 340, tout le reste vient du socle commun.
Ce que Proxmox a en propre est montré AVANT de lancer : le VMID, parce
que l'hôte ne dit « déjà pris » qu'après avoir téléchargé l'image, et
l'adresse qui s'en déduit sur un pont interne. Le stockage et le pont sont
lus sur l'hôte, jamais devinés. L'icône 🗄 distingue l'entrée du menu de
celle qui déploie ici même.
--- EN ---
Deploying on Proxmox meant twenty questions, one after another, never
seeing the whole plan. It now has the same screen as QEMU/KVM — catalogue
left, plan right, totals below — and a key to fall back to the questions.
It repeats none of it: 690 lines against 1,340, everything else comes
from the shared foundation.
What belongs to Proxmox is shown BEFORE launching: the VMID, because the
host only says "already taken" after downloading the image, and the
address derived from it on an internal bridge. Storage and bridge are read
from the host, never guessed. The 🗄 icon tells the menu entry apart from
the one that deploys right here.
Assisted-by: Claude Opus 5
2026-08-23 18:01:28 -04:00
|
|
|
|
if ident in ("f_storage", "f_bridge"):
|
|
|
|
|
|
self._refresh_after()
|
|
|
|
|
|
return
|
|
|
|
|
|
# Réglage commun : « libre… » révèle la saisie, une valeur
|
|
|
|
|
|
# s'applique à toutes les VM non figées.
|
|
|
|
|
|
champ = SELECT_TO_FIELD.get(ident)
|
|
|
|
|
|
if champ:
|
|
|
|
|
|
if event.value is FREE:
|
|
|
|
|
|
self._show_free(champ, True)
|
|
|
|
|
|
self.query_one(RES_FIELDS[champ][1], Input).focus()
|
|
|
|
|
|
elif event.value is not SELECT_NULL:
|
|
|
|
|
|
self._show_free(champ, False)
|
|
|
|
|
|
self.custom[champ] = event.value
|
|
|
|
|
|
self._clear_overrides((champ,))
|
|
|
|
|
|
self._refresh_after(remonter=True)
|
|
|
|
|
|
return
|
|
|
|
|
|
# Réglage d'UNE rangée.
|
|
|
|
|
|
if ident.startswith("v") and "_" in ident:
|
|
|
|
|
|
rang, champ = ident[1:].split("_", 1)
|
|
|
|
|
|
if not rang.isdigit() or champ not in RES_FIELDS:
|
|
|
|
|
|
return
|
|
|
|
|
|
index = int(rang)
|
|
|
|
|
|
if event.value is FREE:
|
|
|
|
|
|
self._row_free(index, champ, True)
|
|
|
|
|
|
self._set_override(
|
|
|
|
|
|
index, champ, self._read_row_free(index, champ)
|
|
|
|
|
|
)
|
|
|
|
|
|
elif event.value is not SELECT_NULL:
|
|
|
|
|
|
# L'écho du montage n'est pas une saisie : sans ce test,
|
|
|
|
|
|
# les trois champs de chaque VM se surchargeaient dès
|
|
|
|
|
|
# l'affichage et toutes les rangées portaient la marque ✎.
|
|
|
|
|
|
if self._row_echo(index, champ, event.value):
|
|
|
|
|
|
return
|
|
|
|
|
|
self._row_free(index, champ, False)
|
|
|
|
|
|
self._set_override(index, champ, event.value)
|
|
|
|
|
|
self._refresh_after()
|
|
|
|
|
|
|
|
|
|
|
|
def on_button_pressed(self, event) -> None:
|
|
|
|
|
|
ident = event.button.id or ""
|
|
|
|
|
|
if ident == "go":
|
|
|
|
|
|
self.action_deploy()
|
|
|
|
|
|
elif ident == "no":
|
|
|
|
|
|
self.action_cancel()
|
|
|
|
|
|
elif ident == "prompts":
|
|
|
|
|
|
# Retour aux invites textuelles : {} n'est pas None, et
|
|
|
|
|
|
# l'appelant sait faire la différence entre « annulé » et
|
|
|
|
|
|
# « pose-moi les questions à l'ancienne ».
|
|
|
|
|
|
self.result = {}
|
|
|
|
|
|
self.exit()
|
|
|
|
|
|
elif ident.startswith("p") and ident[1:].isdigit():
|
|
|
|
|
|
self._add_copy(int(ident[1:]), 1)
|
|
|
|
|
|
elif ident.startswith("m") and ident[1:].isdigit():
|
|
|
|
|
|
self._add_copy(int(ident[1:]), -1)
|
|
|
|
|
|
elif ident.startswith("r") and ident[1:].isdigit():
|
|
|
|
|
|
self._rename(int(ident[1:]))
|
|
|
|
|
|
elif ident.startswith("l") and ident[1:].isdigit():
|
|
|
|
|
|
index = int(ident[1:])
|
|
|
|
|
|
self._set_lock(index, self._row_key(index) not in self.locked)
|
|
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------- #
|
|
|
|
|
|
# Les actions
|
|
|
|
|
|
# ---------------------------------------------------------------- #
|
|
|
|
|
|
def _install(self):
|
|
|
|
|
|
if not self.query_one("#f_install", Checkbox).value:
|
|
|
|
|
|
return None
|
|
|
|
|
|
index = self.query_one("#f_profile_install", Select).value
|
|
|
|
|
|
label, cmd = (
|
|
|
|
|
|
profiles[index]
|
|
|
|
|
|
if profiles and isinstance(index, int)
|
|
|
|
|
|
else ("", "")
|
|
|
|
|
|
)
|
|
|
|
|
|
return {
|
|
|
|
|
|
"branch": self.query_one("#f_branch", Select).value,
|
|
|
|
|
|
"label": label,
|
|
|
|
|
|
"cmd": cmd,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
def _form_values(self):
|
|
|
|
|
|
cle = self.query_one("#f_key", Input).value.strip()
|
|
|
|
|
|
return {
|
|
|
|
|
|
"host": ctx["host"],
|
|
|
|
|
|
"storage": self._storage(),
|
|
|
|
|
|
"bridge": self._bridge(),
|
[FIX] proxmox : sh: 1: Syntax error: "(" unexpected
Rapporté. La chaîne, en trois maillons : sur un hôte sans pont, « ip -o link
show type bridge » ne rend RIEN, la sortie ne contient donc que
l'avertissement de ssh sur la clé d'hôte — que le lecteur a pris pour un nom
de pont. « (ED25519) » s'est retrouvé dans « --net0 virtio,bridge=… », enrobé
de « sudo sh -c », et dash a répondu ce que l'utilisateur a lu. Le bruit de
ssh est maintenant retiré à la source, et un pont doit avoir la forme d'un
lien pour en être un.
Éprouvé sur l'hôte réel, VM créée puis détruite : le pont ne montait pas
(ifupdown2 accuse « another instance » quand /run/network manque — un
mensonge), le noyau Debian n'a ni module bridge ni table NAT, et une VM en
adresse fixe n'avait aucun résolveur. Le déploiement écrit désormais un
journal par VM sous ~/.erplibre/proxmox-deploy et en donne le chemin.
--- EN ---
Reported. The chain, in three links: on a host with no bridge, "ip -o link
show type bridge" returns NOTHING, so the output holds only ssh's host-key
warning — which the parser took for a bridge name. "(ED25519)" landed in
"--net0 virtio,bridge=…", wrapped in "sudo sh -c", and dash answered what the
user read. Ssh's noise is now stripped at the source, and a bridge must have
the shape of a link to be one.
Proven on the real host, VM created then destroyed: the bridge would not come
up (ifupdown2 claims "another instance" when /run/network is missing — a lie),
the Debian kernel has neither the bridge module nor the NAT table, and a
statically addressed VM had no resolver at all. Deployment now writes one log
per VM under ~/.erplibre/proxmox-deploy and prints its path.
Assisted-by: Claude Opus 5
2026-08-24 04:17:36 -04:00
|
|
|
|
"nameservers": ctx.get("nameservers") or (),
|
[ADD] proxmox : un écran pour déployer sur un hôte distant
Le déploiement Proxmox se faisait par vingt questions, l'une après
l'autre, sans jamais voir le plan entier. Il a maintenant le même écran
que QEMU/KVM — catalogue à gauche, plan à droite, totaux dessous — et une
touche pour revenir aux questions. Il n'en redit rien : 690 lignes contre
1 340, tout le reste vient du socle commun.
Ce que Proxmox a en propre est montré AVANT de lancer : le VMID, parce
que l'hôte ne dit « déjà pris » qu'après avoir téléchargé l'image, et
l'adresse qui s'en déduit sur un pont interne. Le stockage et le pont sont
lus sur l'hôte, jamais devinés. L'icône 🗄 distingue l'entrée du menu de
celle qui déploie ici même.
--- EN ---
Deploying on Proxmox meant twenty questions, one after another, never
seeing the whole plan. It now has the same screen as QEMU/KVM — catalogue
left, plan right, totals below — and a key to fall back to the questions.
It repeats none of it: 690 lines against 1,340, everything else comes
from the shared foundation.
What belongs to Proxmox is shown BEFORE launching: the VMID, because the
host only says "already taken" after downloading the image, and the
address derived from it on an internal bridge. Storage and bridge are read
from the host, never guessed. The 🗄 icon tells the menu entry apart from
the one that deploys right here.
Assisted-by: Claude Opus 5
2026-08-23 18:01:28 -04:00
|
|
|
|
"res_label": res_label(self.profile),
|
|
|
|
|
|
"ssh_key": os.path.expanduser(cle) if cle else "",
|
|
|
|
|
|
"start": self.query_one("#f_start", Checkbox).value,
|
|
|
|
|
|
"add_ssh_config": self.query_one("#f_sshcfg", Checkbox).value,
|
|
|
|
|
|
"install": self._install(),
|
|
|
|
|
|
# Le suivi est demandé au NIVEAU DU DÉPLOIEMENT : une VM sans
|
|
|
|
|
|
# ERPLibre se suit aussi (cloud-init, puis relevé système).
|
|
|
|
|
|
"monitor": self.query_one("#f_monitor", Checkbox).value,
|
|
|
|
|
|
"parallelism": self.query_one("#f_par", Select).value,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
def action_deploy(self) -> None:
|
|
|
|
|
|
spec = build_spec(self.vms, noms_pris, self._form_values())
|
|
|
|
|
|
if not spec["vms"]:
|
|
|
|
|
|
self.notify(t("Nothing to deploy."), severity="warning")
|
|
|
|
|
|
return
|
|
|
|
|
|
if not spec["storage"]:
|
|
|
|
|
|
self.notify(
|
|
|
|
|
|
t("No storage able to hold a VM disk."), severity="error"
|
|
|
|
|
|
)
|
|
|
|
|
|
return
|
|
|
|
|
|
if not spec["bridge"]:
|
|
|
|
|
|
self.notify(t("No bridge on the host."), severity="error")
|
|
|
|
|
|
return
|
|
|
|
|
|
self.result = spec
|
|
|
|
|
|
self.exit()
|
|
|
|
|
|
|
|
|
|
|
|
def action_preview(self) -> None:
|
|
|
|
|
|
build = ctx.get("build_command")
|
|
|
|
|
|
if not build:
|
|
|
|
|
|
return
|
|
|
|
|
|
spec = build_spec(self.vms, noms_pris, self._form_values())
|
|
|
|
|
|
lignes = ["\n".join(build(vm, spec)) for vm in spec["vms"]] or [
|
|
|
|
|
|
t("Nothing selected.")
|
|
|
|
|
|
]
|
|
|
|
|
|
self.push_screen(preview_screen()(lignes))
|
|
|
|
|
|
|
|
|
|
|
|
def action_clear_vm(self) -> None:
|
|
|
|
|
|
"""Rend au réglage commun la VM sous le curseur (et son verrou)."""
|
|
|
|
|
|
index = self._focused_row()
|
|
|
|
|
|
if index is None:
|
|
|
|
|
|
return
|
|
|
|
|
|
cle = self._row_key(index)
|
|
|
|
|
|
self.locked.discard(cle)
|
|
|
|
|
|
self.overrides.pop(cle, None)
|
|
|
|
|
|
self._refresh_after(remonter=True)
|
|
|
|
|
|
|
|
|
|
|
|
def action_select_all(self) -> None:
|
|
|
|
|
|
self.query_one("#f_catalog", SelectionList).select_all()
|
|
|
|
|
|
|
|
|
|
|
|
def action_select_none(self) -> None:
|
|
|
|
|
|
self.query_one("#f_catalog", SelectionList).deselect_all()
|
|
|
|
|
|
|
|
|
|
|
|
def action_cancel(self) -> None:
|
|
|
|
|
|
self.result = None
|
|
|
|
|
|
self.exit()
|
|
|
|
|
|
|
|
|
|
|
|
app = ProxmoxForm()
|
|
|
|
|
|
if not run_app:
|
|
|
|
|
|
return app
|
|
|
|
|
|
app.run()
|
|
|
|
|
|
return app.result
|