[ADD] tui qemu: rename a VM with the pencil

A "✎" per row opens an input prefilled with the current name. The given
name becomes an override like any other: it survives recomputation, and
F4 removes it along with the VM's other settings.

An explicit name makes automatic naming GIVE WAY. Switching the VM to
GNOME no longer appends "-gnome": adding a suffix would amount to
correcting the user. Clearing the field restores the automatic name,
copy and desktop suffixes included.

The name becomes the machine's HOSTNAME, so it is validated against
RFC 1123 before being accepted. One capital or one dot too many and
cloud-init silently ignores it -- the VM would stay "ubuntu", and you
would find out once deployed. Capitals are lowered, the rest is refused
with a message.

--- FR ---

Un « ✎ » par rangée ouvre une saisie préremplie du nom courant. Le nom
donné devient une surcharge comme les autres : il survit au recalcul, et
F4 le retire avec le reste des réglages de la VM.

Un nom explicite fait CÉDER le nommage automatique. Passer la VM en
GNOME n'y colle plus « -gnome » : y ajouter un suffixe reviendrait à
corriger l'utilisateur. Vider le champ rend le nom automatique, suffixes
de copie et de bureau compris.

Le nom devient le NOM D'HÔTE de la machine : il est donc validé en
RFC 1123 avant d'être retenu. Une majuscule ou un point de trop et
cloud-init l'ignore en silence — la VM resterait « ubuntu », et on le
découvrirait une fois déployée. Les majuscules sont abaissées, le reste
est refusé avec un message.

Assisted-by: Claude Opus 5
This commit is contained in:
Mathieu Benoit 2026-08-13 00:41:16 -04:00
parent cbdded8fab
commit 17d0d60741

View file

@ -180,6 +180,20 @@ def apply_overrides(vms, entries, overrides):
return vms
def clean_hostname(value):
"""Nom d'hote valide (RFC 1123) tire de la saisie, ou None.
Le nom d'une VM devient son NOM D'HOTE : une majuscule ou un point de
trop et cloud-init l'ignore en silence, la machine reste « ubuntu ».
Mieux vaut refuser ici que le decouvrir sur une VM deja deployee."""
txt = str(value or "").strip().lower()
if not txt or len(txt) > 63:
return None
if not re.fullmatch(r"[a-z0-9]([a-z0-9-]*[a-z0-9])?", txt):
return None
return txt
def vm_name(base, desktop, suffixes):
"""Nom de VM, suffixé du bureau quand il y en a un.
@ -211,7 +225,11 @@ def build_vms(
)
# APRÈS les surcharges : c'est là seulement que le type de chaque VM est
# connu, puisqu'il se choisit machine par machine.
for vm in vms:
for vm, e in zip(vms, entries):
if (overrides or {}).get(entry_key(e), {}).get("name"):
# Nom donne a la main : il gagne, sans suffixe ajoute. Y coller
# « -gnome » reviendrait a corriger l'utilisateur.
continue
vm["name"] = vm_name(vm["name"], vm.get("desktop"), suffixes)
return vms
@ -368,6 +386,38 @@ def run_deploy_form(ctx, run_app: bool = True):
f"RAM≥{e['ram']}Mo {e['disk']}"
)
class RenameScreen(ModalScreen):
"""Nom d'une VM. Vide = revenir au nom automatique."""
BINDINGS = [("escape", "cancel", t("Cancel"))]
def __init__(self, name, auto):
super().__init__()
self._name = name
self._auto = auto
def compose(self) -> ComposeResult:
with Vertical(id="renbox"):
yield Static(t("Rename the VM"), id="rentitle")
yield Input(value=self._name, id="renval")
yield Static(
f" {t('Empty = back to the automatic name:')} "
f"{self._auto}",
id="renhint",
)
with Horizontal(id="renbtns"):
yield Button(t("Cancel"), id="ren_no")
yield Button(t("Rename"), variant="primary", id="ren_ok")
def on_button_pressed(self, event) -> None:
if event.button.id != "ren_ok":
self.dismiss(None)
return
self.dismiss(self.query_one("#renval", Input).value)
def action_cancel(self) -> None:
self.dismiss(None)
class PreviewScreen(ModalScreen):
"""Aperçu des commandes qui seraient lancées (aucune exécution)."""
@ -427,6 +477,14 @@ def run_deploy_form(ctx, run_app: bool = True):
.vmrow Select { width: 15; }
.vmrow Input { width: 11; }
#reslabel { color: $text-muted; }
RenameScreen { align: center middle; }
#renbox {
width: 60; height: auto; padding: 1 2;
border: thick $accent; background: $surface;
}
#rentitle { color: $accent; text-style: bold; }
#renhint { color: $text-muted; }
#renbtns { height: auto; padding-top: 1; }
PreviewScreen { align: center middle; }
#prevbox {
width: 90%; height: 70%; padding: 1 2;