1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-08 19:26:48 +00:00
webvirtcloud/instances/tests/test_update_console.py
catborise 301b73155d 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.
2026-10-30 14:55:57 +03:00

90 lines
5 KiB
Python

"""Console settings: each change is saved to the persistent definition, the
password only by the action chosen for it, and the VNC settings only while
the saved console type is VNC."""
from unittest.mock import patch
from computes.models import Compute
from django.contrib.auth import get_user_model
from django.contrib.messages import get_messages
from django.test import TestCase, override_settings
from django.urls import reverse
from instances.forms import ConsoleForm
from instances.models import Instance
VNC = {"console_type": "vnc", "listen_on": "127.0.0.1", "keymap": "auto", "password_action": "keep"}
class UpdateConsoleTestCase(TestCase):
def setUp(self):
admin = get_user_model().objects.create_superuser("console_admin", "c@example.com", "pw")
self.client.force_login(admin)
compute = Compute.objects.create(name="console-compute", hostname="127.0.0.1", login="", password="", type=4)
self.instance = Instance.objects.create(
compute=compute, name="console-vm", uuid="11111111-2222-3333-4444-555555555555"
)
def post(self, data, console_type="vnc"):
"""console_type: the persistent definition's; the live one is VNC."""
with patch("instances.models.wvmInstance") as wvm:
proxy = wvm.return_value
proxy.get_console_type.side_effect = lambda config=False: console_type if config else "vnc"
proxy.get_console_listener_addr.return_value = "127.0.0.1"
proxy.get_console_keymap.return_value = ""
proxy.set_console_passwd.return_value = True
response = self.client.post(reverse("instances:update_console", args=[self.instance.id]), data)
return proxy, [str(m) for m in get_messages(response.wsgi_request)]
def test_console_settings_are_applied(self):
proxy, _ = self.post({**VNC, "listen_on": "0.0.0.0", "keymap": "de"})
proxy.set_console_keymap.assert_called_once_with("de")
proxy.set_console_listener_addr.assert_called_once_with("0.0.0.0")
proxy.set_console_passwd.assert_not_called()
def test_the_password_changes_by_the_action_chosen(self):
for action, password in (("set", " a b c "), ("set", "éééé"), ("none", "")):
with self.subTest(action, password=password):
proxy, _ = self.post({**VNC, "password_action": action, "password": password})
proxy.set_console_passwd.assert_called_once_with(password)
proxy, _ = self.post({**VNC, "password_action": "generate"})
(generated,), _ = proxy.set_console_passwd.call_args
self.assertEqual(len(generated), 8)
def test_a_password_vnc_cannot_take_is_refused_and_nothing_is_saved(self):
for password in ("", "123456789", "ééééé"): # libvirt counts bytes: é is two
with self.subTest(password=password):
proxy, messages = self.post({**VNC, "keymap": "de", "password_action": "set", "password": password})
proxy.set_console_passwd.assert_not_called()
proxy.set_console_keymap.assert_not_called()
self.assertTrue(messages[0].startswith("Console settings were not saved"), messages)
@override_settings(SERIAL_CONSOLE_ENABLED=True)
def test_a_new_serial_console_ignores_the_vnc_settings(self):
# even ones that would be refused: an empty new password, an unknown keymap
for vnc in ({"keymap": "de", "password_action": "none"},
{"keymap": "xx", "password_action": "set", "password": ""}):
with self.subTest(vnc):
proxy, messages = self.post({**VNC, **vnc, "console_type": "pty"})
self.assertEqual(messages, [])
proxy.set_console_type.assert_called_once_with("pty")
proxy.set_console_keymap.assert_not_called()
proxy.set_console_passwd.assert_not_called()
@override_settings(SERIAL_CONSOLE_ENABLED=True)
def test_a_saved_serial_console_shows_and_takes_no_vnc_settings(self):
# the running VM still has VNC: the type to compare with is the saved one
proxy, messages = self.post({"console_type": "pty"}, console_type="pty")
self.assertEqual(messages, [])
proxy.set_console_type.assert_not_called()
proxy, _ = self.post({"console_type": "vnc", "keymap": "de"}, console_type="pty")
proxy.set_console_type.assert_called_once_with("vnc")
proxy.set_console_keymap.assert_not_called()
with patch("instances.models.wvmInstance") as wvm:
wvm.return_value.get_console_type.side_effect = lambda config=False: "pty" if config else "vnc"
form = ConsoleForm(instance=Instance.objects.get(pk=self.instance.pk), is_superuser=True)
self.assertEqual((form["console_type"].value(), sorted(form.fields)), ("pty", ["console_type"]))
@override_settings(SERIAL_CONSOLE_ENABLED=False)
def test_serial_is_not_offered_while_the_serial_console_is_off(self):
proxy, _ = self.post({**VNC, "console_type": "pty"})
proxy.set_console_type.assert_not_called()