mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-08 19:26:48 +00:00
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.
90 lines
5 KiB
Python
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()
|