1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-11 12:46:49 +00:00

feat(instances): a VM's console settings in two cards, with a password action

Settings > Console was one horizontal form, and on a running VM its
Update button was disabled. The owner chose this layout from three
rendered options: the console type on top, then a "VNC connection" card
(listen address, keymap) and a "VNC password" card.

- The password is changed by an action: keep it, generate a new one,
  set a new one or remove it. This replaces the Generate and Clear
  boxes and their precedence, and an emptied field no longer removes
  the password silently. The pane says whether a password is set; the
  form no longer carries the password itself.
- A VNC password takes at most 8 bytes: libvirt refuses a longer one
  ("VNC password is 9 characters long, only 8 permitted"). Generate and
  a new VM's random password now make 8 characters; they made 12, which
  libvirt 11 refuses. A set password keeps its spaces.
- Every check is in the form, so a refused save changes nothing; before,
  a refused password removal still saved the other settings.
- The settings can be saved while the VM runs: they are in the
  persistent definition. A line says the running console keeps its
  settings until the VM is shut down and started.
- The console type shown and compared is the persistent one; it was the
  running VM's, so a pending switch to Serial showed VNC and a later
  save could switch again.
- While the saved type is Serial, the VNC settings are not shown or
  taken; choosing Serial ignores the posted VNC settings.
This commit is contained in:
catborise 2026-10-30 12:26:22 +03:00
parent 20cfc25367
commit 301b73155d
12 changed files with 248 additions and 126 deletions

View file

@ -21,24 +21,35 @@ class FlavorForm(forms.ModelForm):
class ConsoleForm(forms.Form):
console_type = forms.ChoiceField(label=_("Console type"), required=False)
listen_on = forms.ChoiceField(label=_("Listen on"))
generate_password = forms.BooleanField(label=_("Generate password"), required=False)
clear_password = forms.BooleanField(label=_("Clear password"), required=False)
password = forms.CharField(
label=_("Password"),
widget=forms.PasswordInput(render_value=True),
required=False
console_type = forms.ChoiceField(
label=_("Console type"), required=False, widget=forms.Select(attrs={"class": "form-select"})
)
password_action = forms.ChoiceField(
label=_("Password"),
choices=[
("keep", _("Keep the password")),
("generate", _("Generate a new one")),
("set", _("Set a new one")),
("none", _("No password")),
],
widget=forms.RadioSelect(attrs={"class": "form-check-input"}),
)
password = forms.CharField(
label=_("New password"),
widget=forms.PasswordInput(attrs={"class": "form-control", "autocomplete": "new-password"}),
required=False,
strip=False,
)
keymap = forms.ChoiceField(label=_("Keymap"), required=False)
def __init__(self, *args, instance, can_set_listener, **kwargs):
def __init__(self, *args, instance, is_superuser, **kwargs):
# the VM's settings as the initial values: a save changes only the fields edited
self.instance = instance
self.is_superuser = is_superuser
listener = instance.config_console_listener_address
kwargs["initial"] = {
"console_type": instance.console_type,
"console_type": instance.config_console_type,
"listen_on": listener,
"password": instance.config_console_passwd,
"password_action": "keep",
"keymap": instance.config_console_keymap or "auto",
}
super(ConsoleForm, self).__init__(*args, **kwargs)
@ -46,6 +57,11 @@ class ConsoleForm(forms.Form):
if settings.SERIAL_CONSOLE_ENABLED:
types.append(("pty", _("Serial (xterm; the guest needs a getty on ttyS0)")))
self.fields["console_type"].choices = types
# the VNC settings are shown and saved while the console is VNC; a
# switch to Serial ignores them
if "pty" in (instance.config_console_type, self.data.get("console_type")):
del self.fields["password_action"], self.fields["password"]
return
keymap_choices = [("auto", _("Auto"))] + list((c, c) for c in QEMU_KEYMAPS)
# Only administrators choose the listen address: beyond localhost the
# console is reachable from the network. A disabled field ignores
@ -57,10 +73,32 @@ class ConsoleForm(forms.Form):
self.fields["listen_on"] = forms.ChoiceField(
label=_("Listen on"),
choices=listeners,
disabled=not can_set_listener,
required=can_set_listener,
disabled=not is_superuser,
required=is_superuser,
widget=forms.Select(attrs={"class": "form-select"}),
)
self.fields["keymap"] = forms.ChoiceField(label=_("Keymap"), choices=keymap_choices)
self.fields["keymap"] = forms.ChoiceField(
label=_("Keymap"), choices=keymap_choices, widget=forms.Select(attrs={"class": "form-select"})
)
def clean(self):
data = super().clean()
action = data.get("password_action")
if action == "set":
password = data.get("password", "")
if not password:
self.add_error("password", _("Enter the new password."))
# VNC uses 8 bytes; libvirt refuses a longer password
elif len(password.encode()) > 8:
self.add_error("password", _("A VNC password takes at most 8 bytes."))
# A console that listens beyond localhost (set by an administrator)
# keeps its password unless an administrator removes it.
if action == "none" and not self.is_superuser and not self.instance.proxy.console_listens_locally():
self.add_error(
"password_action",
_("The console listens beyond localhost; only an administrator can remove its password."),
)
return data
class NewVMForm(forms.ModelForm):