# Copyright 2014 Budapest University of Technology and Economics (BME IK) # # This file is part of CIRCLE Cloud. # # CIRCLE is free software: you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) # any later version. # # CIRCLE is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along # with CIRCLE. If not, see <http://www.gnu.org/licenses/>. from __future__ import absolute_import from datetime import timedelta from urlparse import urlparse from django.contrib.auth.forms import ( AuthenticationForm, PasswordResetForm, SetPasswordForm, PasswordChangeForm, ) from django.contrib.auth.models import User, Group from django.core.validators import URLValidator from django.core.exceptions import PermissionDenied, ValidationError import autocomplete_light from crispy_forms.helper import FormHelper from crispy_forms.layout import ( Layout, Div, BaseInput, Field, HTML, Submit, TEMPLATE_PACK, ) from crispy_forms.utils import render_field from django import forms from django.contrib.auth.forms import UserCreationForm as OrgUserCreationForm from django.forms.widgets import TextInput, HiddenInput from django.template import Context from django.template.loader import render_to_string from django.utils.html import escape from django.utils.translation import ugettext_lazy as _ from sizefield.widgets import FileSizeWidget from django.core.urlresolvers import reverse_lazy from django_sshkey.models import UserKey from firewall.models import Vlan, Host from vm.models import ( InstanceTemplate, Lease, InterfaceTemplate, Node, Trait, Instance ) from django.contrib.admin.widgets import FilteredSelectMultiple from django.contrib.auth.models import Permission from .models import Profile, GroupProfile from circle.settings.base import LANGUAGES, MAX_NODE_RAM from django.utils.translation import string_concat from .validators import domain_validator from dashboard.models import ConnectCommand LANGUAGES_WITH_CODE = ((l[0], string_concat(l[1], " (", l[0], ")")) for l in LANGUAGES) priority_choices = ( (10, _("idle")), (30, _("normal")), (80, _("server")), (100, _("realtime")), ) class VmSaveForm(forms.Form): name = forms.CharField(max_length=100, label=_('Name'), help_text=_('Human readable name of template.')) @property def helper(self): helper = FormHelper(self) helper.form_tag = False return helper def __init__(self, *args, **kwargs): default = kwargs.pop('default', None) super(VmSaveForm, self).__init__(*args, **kwargs) if default: self.fields['name'].initial = default class VmCustomizeForm(forms.Form): name = forms.CharField(widget=forms.TextInput(attrs={ 'class': "form-control", 'style': "max-width: 350px", 'required': "", })) cpu_count = forms.IntegerField(widget=forms.NumberInput(attrs={ 'class': "form-control input-tags cpu-count-input", 'min': 1, 'max': 10, 'required': "", }), min_value=1, max_value=10, ) ram_size = forms.IntegerField(widget=forms.TextInput(attrs={ 'class': "form-control input-tags ram-input", 'min': 128, 'pattern': "\d+", 'max': MAX_NODE_RAM, 'step': 128, 'required': "", }), min_value=128, max_value=MAX_NODE_RAM, ) cpu_priority = forms.ChoiceField( priority_choices, widget=forms.Select(attrs={ 'class': "form-control input-tags cpu-priority-input", }) ) amount = forms.IntegerField(widget=forms.NumberInput(attrs={ 'class': "form-control", 'min': "1", 'style': "max-width: 60px", 'required': "", }), initial=1, min_value=1) disks = forms.ModelMultipleChoiceField( queryset=None, required=False, widget=forms.SelectMultiple(attrs={ 'class': "form-control", 'id': "vm-create-disk-add-form", }) ) networks = forms.ModelMultipleChoiceField( queryset=None, required=False, widget=forms.SelectMultiple(attrs={ 'class': "form-control", 'id': "vm-create-network-add-vlan", }) ) template = forms.CharField(widget=forms.HiddenInput()) customized = forms.CharField(widget=forms.HiddenInput()) def __init__(self, *args, **kwargs): self.user = kwargs.pop("user", None) self.template = kwargs.pop("template", None) super(VmCustomizeForm, self).__init__(*args, **kwargs) if self.user.has_perm("vm.set_resources"): self.allowed_fields = tuple(self.fields.keys()) # set displayed disk and network list self.fields['disks'].queryset = self.template.disks.all() self.fields['networks'].queryset = Vlan.get_objects_with_level( 'user', self.user) # set initial for disk and network list self.initial['disks'] = self.template.disks.all() self.initial['networks'] = InterfaceTemplate.objects.filter( template=self.template).values_list("vlan", flat=True) # set initial for resources self.initial['cpu_priority'] = self.template.priority self.initial['cpu_count'] = self.template.num_cores self.initial['ram_size'] = self.template.ram_size else: self.allowed_fields = ("name", "template", "customized", ) # initial name and template pk self.initial['name'] = self.template.name self.initial['template'] = self.template.pk self.initial['customized'] = True def _clean_fields(self): for name, field in self.fields.items(): if name in self.allowed_fields: value = field.widget.value_from_datadict( self.data, self.files, self.add_prefix(name)) try: value = field.clean(value) self.cleaned_data[name] = value if hasattr(self, 'clean_%s' % name): value = getattr(self, 'clean_%s' % name)() self.cleaned_data[name] = value except ValidationError as e: self._errors[name] = self.error_class(e.messages) if name in self.cleaned_data: del self.cleaned_data[name] class GroupCreateForm(forms.ModelForm): description = forms.CharField(label=_("Description"), required=False, widget=forms.Textarea(attrs={'rows': 3})) def __init__(self, *args, **kwargs): new_groups = kwargs.pop('new_groups', None) super(GroupCreateForm, self).__init__(*args, **kwargs) choices = [('', '--')] if new_groups: choices += [(g, g) for g in new_groups if len(g) <= 64] self.fields['org_id'] = forms.ChoiceField( # TRANSLATORS: directory like in LDAP choices=choices, required=False, label=_('Directory identifier')) if new_groups: self.fields['org_id'].help_text = _( "If you select an item here, the members of this directory " "group will be automatically added to the group at the time " "they log in. Please note that other users (those with " "permissions like yours) may also automatically become a " "group co-owner).") else: self.fields['org_id'].widget = HiddenInput() def save(self, commit=True): if not commit: raise AttributeError('Committing is mandatory.') group = super(GroupCreateForm, self).save() profile = group.profile # multiple blanks were not be unique unlike NULLs are profile.org_id = self.cleaned_data['org_id'] or None profile.description = self.cleaned_data['description'] profile.save() return group @property def helper(self): helper = FormHelper(self) helper.add_input(Submit("submit", _("Create"))) helper.form_tag = False return helper class Meta: model = Group fields = ('name', ) class GroupProfileUpdateForm(forms.ModelForm): def __init__(self, *args, **kwargs): new_groups = kwargs.pop('new_groups', None) superuser = kwargs.pop('superuser', False) super(GroupProfileUpdateForm, self).__init__(*args, **kwargs) if not superuser: choices = [('', '--')] if new_groups: choices += [(g, g) for g in new_groups if len(g) <= 64] self.fields['org_id'] = forms.ChoiceField( choices=choices, required=False, label=_('Directory identifier')) if not new_groups: self.fields['org_id'].widget = HiddenInput() self.fields['description'].widget = forms.Textarea(attrs={'rows': 3}) @property def helper(self): helper = FormHelper(self) helper.add_input(Submit("submit", _("Save"))) helper.form_tag = False return helper def save(self, commit=True): profile = super(GroupProfileUpdateForm, self).save(commit=False) profile.org_id = self.cleaned_data['org_id'] or None if commit: profile.save() return profile class Meta: model = GroupProfile fields = ('description', 'org_id') class HostForm(forms.ModelForm): def setowner(self, user): self.instance.owner = user def __init__(self, *args, **kwargs): super(HostForm, self).__init__(*args, **kwargs) self.helper = FormHelper(self) self.helper.form_show_labels = False self.helper.form_tag = False self.helper.layout = Layout( Div( Div( # host Div( AnyTag( 'h3', HTML(_("Host")), ), css_class="col-sm-3", ), css_class="row", ), Div( # host data Div( # hostname HTML('<label for="node-hostname-box">' 'Name' '</label>'), css_class="col-sm-3", ), Div( # hostname 'hostname', css_class="col-sm-9", ), Div( # mac HTML('<label for="node-mac-box">' 'MAC' '</label>'), css_class="col-sm-3", ), Div( 'mac', css_class="col-sm-9", ), Div( # ip HTML('<label for="node-ip-box">' 'IP' '</label>'), css_class="col-sm-3", ), Div( 'ipv4', css_class="col-sm-9", ), Div( # vlan HTML('<label for="node-vlan-box">' 'VLAN' '</label>'), css_class="col-sm-3", ), Div( 'vlan', css_class="col-sm-9", ), css_class="row", ), ), ) class Meta: model = Host fields = ['hostname', 'vlan', 'mac', 'ipv4', ] class NodeForm(forms.ModelForm): def __init__(self, *args, **kwargs): super(NodeForm, self).__init__(*args, **kwargs) self.helper = FormHelper(self) self.helper.form_show_labels = False self.helper.layout = Layout( Div( Div( Div( Div( AnyTag( 'h3', HTML(_("Node")), ), css_class="col-sm-3", ), css_class="row", ), Div( Div( # nodename HTML('<label for="node-nodename-box">' 'Name' '</label>'), css_class="col-sm-3", ), Div( 'name', css_class="col-sm-9", ), css_class="row", ), Div( Div( # priority HTML('<label for="node-nodename-box">' 'Priority' '</label>'), css_class="col-sm-3", ), Div( 'priority', css_class="col-sm-9", ), css_class="row", ), Div( Div( # enabled HTML('<label for="node-nodename-box">' 'Enabled' '</label>'), css_class="col-sm-3", ), Div( 'enabled', css_class="col-sm-9", ), css_class="row", ), Div( # nested host HTML("""{% load crispy_forms_tags %} {% crispy hostform %} """) ), Div( Div( AnyTag( # tip: don't try to use Button class "button", AnyTag( "i", css_class="fa fa-play" ), HTML("Start"), css_id="node-create-submit", css_class="btn btn-success", ), css_class="col-sm-12 text-right", ), css_class="row", ), css_class="col-sm-11", ), css_class="row", ), ) class Meta: model = Node fields = ['name', 'priority', 'enabled', ] class TemplateForm(forms.ModelForm): networks = forms.ModelMultipleChoiceField( queryset=None, required=False, label=_("Networks")) num_cores = forms.IntegerField(widget=forms.NumberInput(attrs={ 'class': "form-control input-tags cpu-count-input", 'min': 1, 'max': 10, 'required': "", }), min_value=1, max_value=10, ) ram_size = forms.IntegerField(widget=forms.NumberInput(attrs={ 'class': "form-control input-tags ram-input", 'min': 128, 'max': MAX_NODE_RAM, 'step': 128, 'required': "", }), min_value=128, max_value=MAX_NODE_RAM, ) priority = forms.ChoiceField(priority_choices, widget=forms.Select(attrs={ 'class': "form-control input-tags cpu-priority-input", })) def __init__(self, *args, **kwargs): self.user = kwargs.pop("user", None) super(TemplateForm, self).__init__(*args, **kwargs) self.fields['networks'].queryset = Vlan.get_objects_with_level( 'user', self.user) data = self.data.copy() data['owner'] = self.user.pk self.data = data if self.instance.pk: n = self.instance.interface_set.values_list("vlan", flat=True) self.initial['networks'] = n if self.instance.pk and not self.instance.has_level(self.user, 'owner'): self.allowed_fields = () else: self.allowed_fields = ( 'name', 'access_method', 'description', 'system', 'tags', 'arch', 'lease', 'has_agent') if (self.user.has_perm('vm.change_template_resources') or not self.instance.pk): self.allowed_fields += tuple(set(self.fields.keys()) - set(['raw_data'])) if self.user.is_superuser: self.allowed_fields += ('raw_data', ) for name, field in self.fields.items(): if name not in self.allowed_fields: field.widget.attrs['disabled'] = 'disabled' if not self.instance.pk and len(self.errors) < 1: self.initial['num_cores'] = 1 self.initial['priority'] = 10 self.initial['ram_size'] = 512 self.initial['max_ram_size'] = 512 lease_queryset = ( Lease.get_objects_with_level("operator", self.user).distinct() | Lease.objects.filter(pk=self.instance.lease_id).distinct()) self.fields["lease"].queryset = lease_queryset self.fields['raw_data'].validators.append(domain_validator) def clean_owner(self): if self.instance.pk is not None: return User.objects.get(pk=self.instance.owner.pk) return self.user def clean_max_ram_size(self): return self.cleaned_data.get("ram_size", 0) def _clean_fields(self): try: old = InstanceTemplate.objects.get(pk=self.instance.pk) except InstanceTemplate.DoesNotExist: old = None for name, field in self.fields.items(): if name in self.allowed_fields: value = field.widget.value_from_datadict( self.data, self.files, self.add_prefix(name)) try: value = field.clean(value) self.cleaned_data[name] = value if hasattr(self, 'clean_%s' % name): value = getattr(self, 'clean_%s' % name)() self.cleaned_data[name] = value except ValidationError as e: self._errors[name] = self.error_class(e.messages) if name in self.cleaned_data: del self.cleaned_data[name] elif old: if name == 'networks': self.cleaned_data[name] = [ i.vlan for i in self.instance.interface_set.all()] else: self.cleaned_data[name] = getattr(old, name) if "req_traits" not in self.allowed_fields: self.cleaned_data['req_traits'] = self.instance.req_traits.all() def save(self, commit=True): data = self.cleaned_data self.instance.max_ram_size = data.get('ram_size') instance = super(TemplateForm, self).save(commit=True) # create and/or delete InterfaceTemplates networks = InterfaceTemplate.objects.filter( template=self.instance).values_list("vlan", flat=True) for m in data['networks']: if not m.has_level(self.user, "user"): raise PermissionDenied() if m.pk not in networks: InterfaceTemplate(vlan=m, managed=m.managed, template=self.instance).save() InterfaceTemplate.objects.filter( template=self.instance).exclude( vlan__in=data['networks']).delete() return instance @property def helper(self): submit_kwargs = {} if self.instance.pk and not self.instance.has_level(self.user, 'owner'): submit_kwargs['disabled'] = None helper = FormHelper() return helper class Meta: model = InstanceTemplate exclude = ('state', 'disks', ) widgets = { 'system': forms.TextInput, 'max_ram_size': forms.HiddenInput, 'parent': forms.Select(attrs={'disabled': ""}), } class LeaseForm(forms.ModelForm): def __init__(self, *args, **kwargs): super(LeaseForm, self).__init__(*args, **kwargs) self.generate_fields() # e2ae8b048e7198428f696375b8bdcd89e90002d1/django/utils/timesince.py#L10 def get_intervals(self, delta_seconds): chunks = ( (60 * 60 * 24 * 30, "months"), (60 * 60 * 24 * 7, "weeks"), (60 * 60 * 24, "days"), (60 * 60, "hours"), ) for i, (seconds, name) in enumerate(chunks): count = delta_seconds // seconds if count != 0: break re = {'%s' % name: count} if i + 1 < len(chunks) and i > 0: seconds2, name2 = chunks[i + 1] count2 = (delta_seconds - (seconds * count)) // seconds2 if count2 != 0: re['%s' % name2] = count2 return re def generate_fields(self): intervals = ["hours", "days", "weeks", "months"] methods = ["suspend", "delete"] # feels redundant but these lines are so long s = (self.instance.suspend_interval.total_seconds() if self.instance.pk else 0) d = (self.instance.delete_interval.total_seconds() if self.instance.pk else 0) seconds = { 'suspend': s, 'delete': d } initial = { 'suspend': self.get_intervals(int(seconds['suspend'])), 'delete': self.get_intervals(int(seconds['delete'])) } for m in methods: for idx, i in enumerate(intervals): self.fields["%s_%s" % (m, i)] = forms.IntegerField( min_value=0, widget=NumberInput, initial=initial[m].get(i, 0)) def save(self, commit=True): data = self.cleaned_data suspend_seconds = timedelta( hours=data['suspend_hours'], days=(data['suspend_days'] + data['suspend_months'] % 12 * 30 + data['suspend_months'] / 12 * 365), weeks=data['suspend_weeks'], ) delete_seconds = timedelta( hours=data['delete_hours'], days=(data['delete_days'] + data['delete_months'] % 12 * 30 + data['delete_months'] / 12 * 365), weeks=data['delete_weeks'], ) self.instance.delete_interval = delete_seconds self.instance.suspend_interval = suspend_seconds instance = super(LeaseForm, self).save(commit=False) if commit: instance.save() return instance @property def helper(self): helper = FormHelper() helper.layout = Layout( Field('name'), Field("suspend_interval_seconds", type="hidden", value="0"), Field("delete_interval_seconds", type="hidden", value="0"), HTML(string_concat("<label>", _("Suspend in"), "</label>")), Div( NumberField("suspend_hours", css_class="form-control"), Div( HTML(_("hours")), css_class="input-group-addon", ), NumberField("suspend_days", css_class="form-control"), Div( HTML(_("days")), css_class="input-group-addon", ), NumberField("suspend_weeks", css_class="form-control"), Div( HTML(_("weeks")), css_class="input-group-addon", ), NumberField("suspend_months", css_class="form-control"), Div( HTML(_("months")), css_class="input-group-addon", ), css_class="input-group interval-input", ), HTML(string_concat("<label>", _("Delete in"), "</label>")), Div( NumberField("delete_hours", css_class="form-control"), Div( HTML(_("hours")), css_class="input-group-addon", ), NumberField("delete_days", css_class="form-control"), Div( HTML(_("days")), css_class="input-group-addon", ), NumberField("delete_weeks", css_class="form-control"), Div( HTML(_("weeks")), css_class="input-group-addon", ), NumberField("delete_months", css_class="form-control"), Div( HTML(_("months")), css_class="input-group-addon", ), css_class="input-group interval-input", ) ) helper.add_input(Submit("submit", _("Save changes"))) return helper class Meta: model = Lease class VmRenewForm(forms.Form): force = forms.BooleanField(required=False, label=_( "Set expiration times even if they are shorter than " "the current value.")) save = forms.BooleanField(required=False, label=_( "Save selected lease.")) def __init__(self, *args, **kwargs): choices = kwargs.pop('choices') default = kwargs.pop('default') super(VmRenewForm, self).__init__(*args, **kwargs) self.fields.insert(0, 'lease', forms.ModelChoiceField( queryset=choices, initial=default, required=False, empty_label=None, label=_('Length'))) if len(choices) < 2: self.fields['lease'].widget = HiddenInput() self.fields['save'].widget = HiddenInput() @property def helper(self): helper = FormHelper(self) helper.form_tag = False return helper class VmMigrateForm(forms.Form): live_migration = forms.BooleanField( required=False, initial=True, label=_("live migration")) def __init__(self, *args, **kwargs): choices = kwargs.pop('choices') default = kwargs.pop('default') super(VmMigrateForm, self).__init__(*args, **kwargs) self.fields.insert(0, 'to_node', forms.ModelChoiceField( queryset=choices, initial=default, required=False, widget=forms.RadioSelect(), label=_("Node"))) class VmStateChangeForm(forms.Form): interrupt = forms.BooleanField(required=False, label=_( "Forcibly interrupt all running activities."), help_text=_("Set all activities to finished state, " "but don't interrupt any tasks.")) new_state = forms.ChoiceField(Instance.STATUS, label=_( "New status")) reset_node = forms.BooleanField(required=False, label=_("Reset node")) def __init__(self, *args, **kwargs): show_interrupt = kwargs.pop('show_interrupt') status = kwargs.pop('status') super(VmStateChangeForm, self).__init__(*args, **kwargs) if not show_interrupt: self.fields['interrupt'].widget = HiddenInput() self.fields['new_state'].initial = status @property def helper(self): helper = FormHelper(self) helper.form_tag = False return helper class RedeployForm(forms.Form): with_emergency_change_state = forms.BooleanField( required=False, initial=True, label=_("use emergency state change")) @property def helper(self): helper = FormHelper(self) helper.form_tag = False return helper class VmCreateDiskForm(forms.Form): name = forms.CharField(max_length=100, label=_("Name")) size = forms.CharField( widget=FileSizeWidget, initial=(10 << 30), label=_('Size'), help_text=_('Size of disk to create in bytes or with units ' 'like MB or GB.')) def __init__(self, *args, **kwargs): default = kwargs.pop('default', None) super(VmCreateDiskForm, self).__init__(*args, **kwargs) if default: self.fields['name'].initial = default def clean_size(self): size_in_bytes = self.cleaned_data.get("size") if not size_in_bytes.isdigit() and len(size_in_bytes) > 0: raise forms.ValidationError(_("Invalid format, you can use " " GB or MB!")) return size_in_bytes @property def helper(self): helper = FormHelper(self) helper.form_tag = False return helper class VmDiskResizeForm(forms.Form): size = forms.CharField( widget=FileSizeWidget, initial=(10 << 30), label=_('Size'), help_text=_('Size to resize the disk in bytes or with units ' 'like MB or GB.')) def __init__(self, *args, **kwargs): choices = kwargs.pop('choices') self.disk = kwargs.pop('default') super(VmDiskResizeForm, self).__init__(*args, **kwargs) self.fields.insert(0, 'disk', forms.ModelChoiceField( queryset=choices, initial=self.disk, required=True, empty_label=None, label=_('Disk'))) if self.disk: self.fields['disk'].widget = HiddenInput() self.fields['size'].initial += self.disk.size def clean(self): cleaned_data = super(VmDiskResizeForm, self).clean() size_in_bytes = self.cleaned_data.get("size") disk = self.cleaned_data.get('disk') if not size_in_bytes.isdigit() and len(size_in_bytes) > 0: raise forms.ValidationError(_("Invalid format, you can use " " GB or MB!")) if int(size_in_bytes) < int(disk.size): raise forms.ValidationError(_("Disk size must be greater than the " "actual size.")) return cleaned_data @property def helper(self): helper = FormHelper(self) helper.form_tag = False if self.disk: helper.layout = Layout( HTML(_("<label>Disk:</label> %s") % escape(self.disk)), Field('disk'), Field('size')) return helper class VmDiskRemoveForm(forms.Form): def __init__(self, *args, **kwargs): choices = kwargs.pop('choices') self.disk = kwargs.pop('default') super(VmDiskRemoveForm, self).__init__(*args, **kwargs) self.fields.insert(0, 'disk', forms.ModelChoiceField( queryset=choices, initial=self.disk, required=True, empty_label=None, label=_('Disk'))) if self.disk: self.fields['disk'].widget = HiddenInput() @property def helper(self): helper = FormHelper(self) helper.form_tag = False if self.disk: helper.layout = Layout( AnyTag( "div", HTML(_("<label>Disk:</label> %s") % escape(self.disk)), css_class="form-group", ), Field("disk"), ) return helper class VmDownloadDiskForm(forms.Form): name = forms.CharField(max_length=100, label=_("Name"), required=False) url = forms.CharField(label=_('URL'), validators=[URLValidator(), ]) @property def helper(self): helper = FormHelper(self) helper.form_tag = False return helper def clean(self): cleaned_data = super(VmDownloadDiskForm, self).clean() if not cleaned_data['name']: if cleaned_data['url']: cleaned_data['name'] = urlparse( cleaned_data['url']).path.split('/')[-1] if not cleaned_data['name']: raise forms.ValidationError( _("Could not find filename in URL, " "please specify a name explicitly.")) return cleaned_data class VmAddInterfaceForm(forms.Form): def __init__(self, *args, **kwargs): choices = kwargs.pop('choices') super(VmAddInterfaceForm, self).__init__(*args, **kwargs) field = forms.ModelChoiceField( queryset=choices, required=True, label=_('Vlan')) if not choices: field.widget.attrs['disabled'] = 'disabled' field.empty_label = _('No more networks.') self.fields['vlan'] = field @property def helper(self): helper = FormHelper(self) helper.form_tag = False return helper class CircleAuthenticationForm(AuthenticationForm): # fields: username, password @property def helper(self): helper = FormHelper() helper.form_show_labels = False helper.layout = Layout( AnyTag( "div", AnyTag( "span", AnyTag( "i", css_class="fa fa-user", ), css_class="input-group-addon", ), Field("username", placeholder=_("Username"), css_class="form-control"), css_class="input-group", ), AnyTag( "div", AnyTag( "span", AnyTag( "i", css_class="fa fa-lock", ), css_class="input-group-addon", ), Field("password", placeholder=_("Password"), css_class="form-control"), css_class="input-group", ), ) helper.add_input(Submit("submit", _("Sign in"), css_class="btn btn-success")) return helper class CirclePasswordResetForm(PasswordResetForm): # fields: email @property def helper(self): helper = FormHelper() helper.form_show_labels = False helper.layout = Layout( AnyTag( "div", AnyTag( "span", AnyTag( "i", css_class="fa fa-envelope", ), css_class="input-group-addon", ), Field("email", placeholder=_("Email address"), css_class="form-control"), Div( AnyTag( "button", HTML(_("Reset password")), css_class="btn btn-success", ), css_class="input-group-btn", ), css_class="input-group", ), ) return helper class CircleSetPasswordForm(SetPasswordForm): @property def helper(self): helper = FormHelper() helper.add_input(Submit("submit", _("Change password"), css_class="btn btn-success change-password", css_id="submit-password-button")) return helper class LinkButton(BaseInput): """ Used to create a link button descriptor for the {% crispy %} template tag:: back = LinkButton('back', 'Back', reverse_lazy('index')) .. note:: The first argument is also slugified and turned into the id for the submit button. """ template = "bootstrap/layout/linkbutton.html" field_classes = 'btn btn-default' def __init__(self, name, text, url, *args, **kwargs): self.href = url super(LinkButton, self).__init__(name, text, *args, **kwargs) class NumberInput(TextInput): input_type = "number" class NumberField(Field): template = "crispy_forms/numberfield.html" def __init__(self, *args, **kwargs): kwargs['min'] = 0 super(NumberField, self).__init__(*args, **kwargs) class AnyTag(Div): template = "crispy_forms/anytag.html" def __init__(self, tag, *fields, **kwargs): self.tag = tag super(AnyTag, self).__init__(*fields, **kwargs) def render(self, form, form_style, context, template_pack=TEMPLATE_PACK): fields = '' for field in self.fields: fields += render_field(field, form, form_style, context, template_pack=template_pack) return render_to_string(self.template, Context({'tag': self, 'fields': fields})) class WorkingBaseInput(BaseInput): def __init__(self, name, value, input_type="text", **kwargs): self.input_type = input_type self.field_classes = "" # we need this for some reason super(WorkingBaseInput, self).__init__(name, value, **kwargs) class TraitForm(forms.ModelForm): def __init__(self, *args, **kwargs): super(TraitForm, self).__init__(*args, **kwargs) self.helper = FormHelper(self) self.helper.form_show_labels = False self.helper.layout = Layout( Div( Field('name', id="node-details-traits-input", css_class="input-sm input-traits"), Div( Submit("submit", _("Add trait"), css_class="btn btn-primary btn-sm input-traits"), css_class="input-group-btn", ), css_class="input-group", id="node-details-traits-form", ), ) class Meta: model = Trait fields = ['name'] class MyProfileForm(forms.ModelForm): preferred_language = forms.ChoiceField(LANGUAGES_WITH_CODE) class Meta: fields = ('preferred_language', 'email_notifications', 'use_gravatar', ) model = Profile @property def helper(self): helper = FormHelper() helper.add_input(Submit("submit", _("Save"))) return helper def save(self, *args, **kwargs): value = super(MyProfileForm, self).save(*args, **kwargs) return value class UnsubscribeForm(forms.ModelForm): class Meta: fields = ('email_notifications', ) model = Profile @property def helper(self): helper = FormHelper() helper.add_input(Submit("submit", _("Save"))) return helper class CirclePasswordChangeForm(PasswordChangeForm): @property def helper(self): helper = FormHelper() helper.add_input(Submit("submit", _("Change password"), css_class="btn btn-primary", css_id="submit-password-button")) return helper class UserCreationForm(OrgUserCreationForm): class Meta: model = User fields = ("username", 'email', 'first_name', 'last_name') @property def helper(self): helper = FormHelper() helper.layout = Layout('username', 'password1', 'password2', 'email', 'first_name', 'last_name') helper.add_input(Submit("submit", _("Save"))) return helper def save(self, commit=True): user = super(UserCreationForm, self).save(commit=False) user.set_password(self.cleaned_data["password1"]) if commit: user.save() return user class AclUserOrGroupAddForm(forms.Form): name = forms.CharField(widget=autocomplete_light.TextWidget( 'AclUserGroupAutocomplete', autocomplete_js_attributes={'placeholder': _("Name of group or user")}, attrs={'class': 'form-control'})) class TransferOwnershipForm(forms.Form): name = forms.CharField( widget=autocomplete_light.TextWidget( 'AclUserAutocomplete', autocomplete_js_attributes={"placeholder": _("Name of user")}, attrs={'class': 'form-control'}), label=_("E-mail address or identifier of user")) class AddGroupMemberForm(forms.Form): new_member = forms.CharField( widget=autocomplete_light.TextWidget( 'AclUserAutocomplete', autocomplete_js_attributes={"placeholder": _("Name of user")}, attrs={'class': 'form-control'}), label=_("E-mail address or identifier of user")) class UserKeyForm(forms.ModelForm): name = forms.CharField(required=True, label=_('Name')) key = forms.CharField( label=_('Key'), required=True, help_text=_('For example: ssh-rsa AAAAB3NzaC1yc2ED...'), widget=forms.Textarea(attrs={'rows': 5})) class Meta: fields = ('name', 'key') model = UserKey @property def helper(self): helper = FormHelper() helper.add_input(Submit("submit", _("Save"))) return helper def __init__(self, *args, **kwargs): self.user = kwargs.pop("user", None) super(UserKeyForm, self).__init__(*args, **kwargs) def clean(self): if self.user: self.instance.user = self.user return super(UserKeyForm, self).clean() class ConnectCommandForm(forms.ModelForm): class Meta: fields = ('name', 'access_method', 'template') model = ConnectCommand def __init__(self, *args, **kwargs): self.user = kwargs.pop("user") super(ConnectCommandForm, self).__init__(*args, **kwargs) def clean(self): if self.user: self.instance.user = self.user return super(ConnectCommandForm, self).clean() class TraitsForm(forms.ModelForm): class Meta: model = Instance fields = ('req_traits', ) @property def helper(self): helper = FormHelper() helper.form_show_labels = False helper.form_action = reverse_lazy("dashboard.views.vm-traits", kwargs={'pk': self.instance.pk}) helper.add_input(Submit("submit", _("Save"), css_class="btn btn-success", )) return helper class RawDataForm(forms.ModelForm): raw_data = forms.CharField(validators=[domain_validator], widget=forms.Textarea(attrs={'rows': 5}), required=False) class Meta: model = Instance fields = ('raw_data', ) @property def helper(self): helper = FormHelper() helper.form_show_labels = False helper.form_action = reverse_lazy("dashboard.views.vm-raw-data", kwargs={'pk': self.instance.pk}) helper.add_input(Submit("submit", _("Save"), css_class="btn btn-success", css_id="submit-password-button")) return helper permissions_filtered = Permission.objects.exclude( codename__startswith="add_").exclude( codename__startswith="delete_").exclude( codename__startswith="change_") class GroupPermissionForm(forms.ModelForm): permissions = forms.ModelMultipleChoiceField( queryset=permissions_filtered, widget=FilteredSelectMultiple(_("permissions"), is_stacked=False) ) class Meta: model = Group fields = ('permissions', ) @property def helper(self): helper = FormHelper() helper.form_show_labels = False helper.form_action = reverse_lazy( "dashboard.views.group-permissions", kwargs={'group_pk': self.instance.pk}) helper.add_input(Submit("submit", _("Save"), css_class="btn btn-success", )) return helper class VmResourcesForm(forms.ModelForm): num_cores = forms.IntegerField(widget=forms.NumberInput(attrs={ 'class': "form-control input-tags cpu-count-input", 'min': 1, 'max': 10, 'required': "", }), min_value=1, max_value=10, ) ram_size = forms.IntegerField(widget=forms.NumberInput(attrs={ 'class': "form-control input-tags ram-input", 'min': 128, 'max': MAX_NODE_RAM, 'step': 128, 'required': "", }), min_value=128, max_value=MAX_NODE_RAM, ) priority = forms.ChoiceField(priority_choices, widget=forms.Select(attrs={ 'class': "form-control input-tags cpu-priority-input", })) def __init__(self, *args, **kwargs): self.can_edit = kwargs.pop("can_edit", None) super(VmResourcesForm, self).__init__(*args, **kwargs) if not self.can_edit: for name, field in self.fields.items(): field.widget.attrs['disabled'] = "disabled" class Meta: model = Instance fields = ('num_cores', 'priority', 'ram_size', ) vm_search_choices = ( ("owned", _("owned")), ("shared", _("shared")), ("all", _("all")), ) class VmListSearchForm(forms.Form): s = forms.CharField(widget=forms.TextInput(attrs={ 'class': "form-control input-tags", 'placeholder': _("Search...") })) stype = forms.ChoiceField(vm_search_choices, widget=forms.Select(attrs={ 'class': "btn btn-default form-control input-tags", 'style': "min-width: 80px;", })) include_deleted = forms.BooleanField(widget=forms.CheckboxInput(attrs={ 'id': "vm-list-search-checkbox", })) def __init__(self, *args, **kwargs): super(VmListSearchForm, self).__init__(*args, **kwargs) # set initial value, otherwise it would be overwritten by request.GET if not self.data.get("stype"): data = self.data.copy() data['stype'] = "all" self.data = data class TemplateListSearchForm(forms.Form): s = forms.CharField(widget=forms.TextInput(attrs={ 'class': "form-control input-tags", 'placeholder': _("Search...") })) stype = forms.ChoiceField(vm_search_choices, widget=forms.Select(attrs={ 'class': "btn btn-default input-tags", })) def __init__(self, *args, **kwargs): super(TemplateListSearchForm, self).__init__(*args, **kwargs) # set initial value, otherwise it would be overwritten by request.GET if not self.data.get("stype"): data = self.data.copy() data['stype'] = "owned" self.data = data