mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-08 11:16:47 +00:00
instances/forms.py and instances/models.py imported values from the webvirtcloud.settings module itself, which bypasses DJANGO_SETTINGS_MODULE (settings-dev, test overrides) and runs the module a second time. They now read django.conf.settings. webvirtcloud/urls.py also loses its fallback that set REST_FRAMEWORK's schema class at import for settings.py copies without it: the template sets it, and an older settings.py is rebuilt from the template when updating.
146 lines
6.4 KiB
Python
146 lines
6.4 KiB
Python
import re
|
|
|
|
from django import forms
|
|
from django.conf import settings
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
from .models import CreateInstance, Flavor
|
|
from .utils import nic_macs
|
|
|
|
|
|
class FlavorForm(forms.ModelForm):
|
|
# the units the create form fills in from a flavor
|
|
memory = forms.IntegerField(label=_("Memory (MiB)"), min_value=1)
|
|
vcpu = forms.IntegerField(label=_("vCPUs"), min_value=1)
|
|
disk = forms.IntegerField(label=_("Disk (GiB)"), min_value=1)
|
|
|
|
class Meta:
|
|
model = Flavor
|
|
fields = "__all__"
|
|
|
|
|
|
class ConsoleForm(forms.Form):
|
|
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,
|
|
)
|
|
|
|
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.config_console_type,
|
|
"listen_on": listener,
|
|
"password_action": "keep",
|
|
"keymap": instance.config_console_keymap or "auto",
|
|
}
|
|
super(ConsoleForm, self).__init__(*args, **kwargs)
|
|
types = [("vnc", "VNC")]
|
|
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 settings.QEMU_KEYMAPS)
|
|
# Only administrators choose the listen address: beyond localhost the
|
|
# console is reachable from the network. A disabled field ignores
|
|
# posted values.
|
|
listeners = list(settings.QEMU_CONSOLE_LISTENER_ADDRESSES)
|
|
# an address set elsewhere (the XML editor) is offered, so a save keeps it
|
|
if listener and listener not in dict(listeners):
|
|
listeners.append((listener, listener))
|
|
self.fields["listen_on"] = forms.ChoiceField(
|
|
label=_("Listen on"),
|
|
choices=listeners,
|
|
disabled=not is_superuser,
|
|
required=is_superuser,
|
|
widget=forms.Select(attrs={"class": "form-select"}),
|
|
)
|
|
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):
|
|
# name = forms.CharField(error_messages={'required': _('No Virtual Machine name has been entered')}, max_length=64)
|
|
# firmware = forms.CharField(max_length=50, required=False)
|
|
# vcpu = forms.IntegerField(error_messages={'required': _('No VCPU has been entered')})
|
|
# disk = forms.IntegerField(required=False)
|
|
# memory = forms.IntegerField(error_messages={'required': _('No RAM size has been entered')})
|
|
# networks = forms.CharField(error_messages={'required': _('No Network pool has been choosen')})
|
|
# nwfilter = forms.CharField(required=False)
|
|
# storage = forms.CharField(max_length=20, required=False)
|
|
# template = forms.CharField(required=False)
|
|
# images = forms.CharField(required=False)
|
|
# cache_mode = forms.CharField(error_messages={'required': _('Please select HDD cache mode')})
|
|
# hdd_size = forms.IntegerField(required=False)
|
|
# meta_prealloc = forms.BooleanField(required=False)
|
|
# virtio = forms.BooleanField(required=False)
|
|
# qemu_ga = forms.BooleanField(required=False)
|
|
# mac = forms.CharField(required=False)
|
|
# graphics = forms.CharField(error_messages={'required': _('Please select a graphics type')})
|
|
# video = forms.CharField(error_messages={'required': _('Please select a video driver')})
|
|
# listener_addr = forms.ChoiceField(required=True, widget=forms.RadioSelect, choices=QEMU_CONSOLE_LISTENER_ADDRESSES)
|
|
class Meta:
|
|
model = CreateInstance
|
|
fields = "__all__"
|
|
exclude = ["compute"]
|
|
|
|
def clean_name(self):
|
|
name = self.cleaned_data["name"]
|
|
have_symbol = re.match("^[a-zA-Z0-9._-]+$", name)
|
|
if not have_symbol:
|
|
raise forms.ValidationError(
|
|
_(
|
|
"The name of the virtual machine must not contain any special characters"
|
|
)
|
|
)
|
|
return name
|
|
|
|
def clean(self):
|
|
# one MAC per NIC at most, each a MAC address, as the job checks
|
|
cleaned = super().clean()
|
|
try:
|
|
nic_macs(cleaned.get("mac") or "", cleaned.get("networks") or "")
|
|
except ValueError as err:
|
|
self.add_error("mac", str(err))
|
|
return cleaned
|