Beispiel #1
0
class DnsRecordForm(SerializerForm):
    """
    Create, update or delete network DNS record.
    """
    _ip = None
    _api_call = dns_record
    template = 'gui/dc/domain_record_form.html'

    id = forms.IntegerField(label=_('ID'),
                            required=True,
                            widget=forms.HiddenInput())
    name = forms.CharField(
        label=_('Name'),
        required=True,
        help_text=_('The full URI the DNS server should pick up on.'),
        widget=forms.TextInput(attrs=TEXT_INPUT_ATTRS))
    content = forms.CharField(
        label=_('Content'),
        required=False,
        # help_text=_('The answer of the DNS query.'),
        widget=forms.TextInput(attrs={'class': 'input-transparent narrow'}))
    type = forms.ChoiceField(label=_('Type'),
                             required=True,
                             choices=Record.TYPE_USED,
                             widget=forms.Select(attrs=SELECT_ATTRS))
    ttl = forms.IntegerField(
        label=_('TTL'),
        required=False,
        help_text=_(
            'How long the DNS client is allowed to remember this record.'),
        widget=NumberInput(attrs={'class': 'input-transparent narrow'}))
    prio = forms.IntegerField(
        label=_('Priority'),
        required=False,
        # help_text=_('Priority used by some record types.'),
        widget=NumberInput(attrs={'class': 'input-transparent narrow'}))
    disabled = forms.BooleanField(
        label=_('Disabled?'),
        required=False,
        help_text=_('If set to true, this record is hidden from DNS clients.'),
        widget=forms.CheckboxInput(attrs={'class': 'normal-check'}))

    def __init__(self, request, domain, record, *args, **kwargs):
        self.domain = domain
        super(DnsRecordForm, self).__init__(request, record, *args, **kwargs)

    def _initial_data(self, request, obj):
        return obj.web_data

    def api_call_args(self, domain_name):
        if self.action == 'create':
            return domain_name,
        else:
            return domain_name, self.cleaned_data['id']
Beispiel #2
0
class BackupDefineForm(SerializerForm, HostnameForm):
    """
    Create or update backup definition.
    """
    _api_call = vm_define_backup

    type = forms.TypedChoiceField(label=_('Backup type'), required=True, choices=BackupDefine.TYPE, coerce=int,
                                  widget=forms.Select(attrs={'class': 'narrow input-select2 disable_create2',
                                                             'required': 'required'}))
    node = forms.ChoiceField(label=_('Backup Node'), required=True,
                             widget=forms.Select(attrs={'class': 'narrow input-select2', 'required': 'required'}))
    zpool = forms.ChoiceField(label=_('Storage'), required=True,
                              widget=forms.Select(attrs={'class': 'narrow input-select2', 'required': 'required'}))
    compression = forms.TypedChoiceField(label=_('Compression'), choices=BackupDefine.COMPRESSION, required=True,
                                         coerce=int, widget=forms.Select(attrs={'class': 'narrow input-select2',
                                                                                'required': 'required'}))
    bwlimit = forms.IntegerField(label=_('Bandwidth limit'), required=False,
                                 help_text=_('Optional transfer rate limit in bytes.'),
                                 widget=NumberInput(attrs={'class': 'input-transparent narrow'}))

    def __init__(self, request, vm, *args, **kwargs):
        super(BackupDefineForm, self).__init__(request, vm, *args, **kwargs)
        self.fields['retention'].help_text = _('Maximum number of backups to keep.')
        self.fields['node'].choices = get_nodes(request, is_backup=True).values_list('hostname', 'hostname')
        self.fields['zpool'].choices = get_zpools(request).filter(node__is_backup=True)\
                                                          .values_list('zpool', 'storage__alias').distinct()
Beispiel #3
0
class ServerReplicaForm(HostnameForm, SerializerForm):
    """
    Server replication settings admin form.
    """
    _api_call = vm_replica

    repname = forms.CharField(
        label=_('Replica Name'),
        required=False,
        widget=forms.TextInput(
            attrs={
                'class': 'input-transparent narrow disable_created',
                'required': 'required'
            }))
    node = forms.TypedChoiceField(
        label=_('Target Node'),
        required=True,
        coerce=str,
        empty_value=None,
        widget=forms.Select(
            attrs={'class': 'input-select2 narrow disable_created2'}))
    sleep_time = forms.IntegerField(
        label=_('Sleep Time'),
        max_value=86400,
        min_value=0,
        required=True,
        help_text=_('Amount of time to pause between two syncs.'),
        widget=NumberInput(attrs={
            'class': 'input-transparent narrow',
            'required': 'required'
        }))
    enabled = forms.BooleanField(
        label=_('Enabled?'),
        required=False,
        widget=forms.CheckboxInput(attrs={'class': 'normal-check'}))
    reserve_resources = forms.BooleanField(
        label=_('Reserve Resources?'),
        required=False,
        help_text=_(
            'Whether to reserve resources (vCPU, RAM) on target compute node'
            '. NOTE: When disabled, the resources will be reserved (and must'
            ' be available) before the failover action.'),
        widget=forms.CheckboxInput(attrs={'class': 'normal-check'}))

    def __init__(self, request, vm, slave_vm, *args, **kwargs):
        self.slave_vm = slave_vm
        vm_nodes = kwargs.pop('vm_nodes', None)
        super(ServerReplicaForm, self).__init__(request, vm, *args, **kwargs)
        if not vm_nodes:
            vm_nodes = get_nodes(request, is_compute=True)
        self.fields['node'].choices = [(i.hostname, i.hostname)
                                       for i in vm_nodes]

    def _initial_data(self, request, obj):
        """Initial data used by 'update'"""
        return self.slave_vm.web_data
Beispiel #4
0
class SnapshotDefineForm(SerializerForm, HostnameForm):
    """
    Create or update snapshot definition.
    """
    _api_call = vm_define_snapshot

    name = forms.RegexField(label=_('Name'), regex=r'^[A-Za-z0-9][A-Za-z0-9\._-]*$', required=True,
                            max_length=8, min_length=1,
                            widget=forms.TextInput(attrs={'class': 'input-transparent narrow',
                                                          'required': 'required', 'pattern': '[A-Za-z0-9\._-]+'}))
    disk_id = forms.TypedChoiceField(label=_('Disk ID'), required=True, coerce=int,
                                     widget=forms.Select(attrs={'class': 'input-select2 narrow',
                                                                'required': 'required'}))
    schedule = forms.CharField(label=_('Schedule'), required=True, max_length=100,
                               help_text=_('Schedule in CRON format. Please use your local time for the hour field '
                                           '(will be internally converted into UTC).'),
                               widget=forms.TextInput(attrs={'class': 'input-transparent narrow',
                                                             'required': 'required'}))
    retention = forms.IntegerField(label=_('Retention'), max_value=65536, min_value=0, required=True,
                                   help_text=_('Maximum number of snapshots to keep.'),
                                   widget=NumberInput(attrs={'class': 'input-transparent narrow',
                                                             'required': 'required'}))
    active = forms.BooleanField(label=_('Active?'), required=False,
                                widget=forms.CheckboxInput(attrs={'class': 'normal-check'}))
    desc = forms.RegexField(label=_('Description'), regex=r'^[^<>%\$&;\'"]*$', max_length=128, required=False,
                            widget=forms.TextInput(attrs={'class': 'input-transparent wide', 'required': ''}))
    fsfreeze = forms.BooleanField(label=_('Freeze filesystem?'), required=False,
                                  help_text=_('Create application-consistent snapshot; '
                                              'Requires QEMU Guest Agent.'),
                                  widget=forms.CheckboxInput(attrs={'class': 'normal-check'}))

    def __init__(self, request, vm, *args, **kwargs):
        super(SnapshotDefineForm, self).__init__(request, vm, *args, **kwargs)
        self.fields['disk_id'].choices = vm_disk_id_choices(vm)

    def clean_schedule(self):
        """Time in schedule in templates is timezone aware. Change to UTC."""
        data = self.cleaned_data.get('schedule')

        try:
            schedule = data.split()
            hour = schedule[1]
            if '*' in hour:
                raise IndexError
        except IndexError:
            return data

        tz = timezone.get_current_timezone()
        now_local = datetime.utcnow().replace(tzinfo=pytz.utc).astimezone(tz)

        def to_utc(match):
            num = int(match.group(0))
            now = now_local.replace(hour=num)
            return str(pytz.utc.normalize(now).hour)

        try:
            schedule[1] = re.sub(r'(\d+)', to_utc, hour)
        except ValueError:
            return data

        return ' '.join(schedule)

    def _initial_data(self, request, vm):
        snapname = request.POST.get('name')
        disk_id = request.POST.get('disk_id')
        res = self.api_call('get', vm, request, args=(vm.hostname, snapname), data={'disk_id': disk_id})

        return res.data['result']

    def _final_data(self, data=None):
        # noinspection PyProtectedMember
        fd = super(SnapshotDefineForm, self)._final_data(data=data)
        # Always include disk_id (required parameter in api call)
        fd['disk_id'] = self.cleaned_data['disk_id']

        return fd
Beispiel #5
0
class AdminServerSettingsForm(ServerSettingsForm):
    """
    Copy of vm_define serializer (api).
    """
    admin = True
    _api_call = vm_define

    tags = TagField(label=_('Tags'), required=False,
                    widget=TagWidget(attrs={'class': 'tags-select2 narrow'}),
                    help_text=_('The tag will be created in case it does not exist.'))
    node = forms.TypedChoiceField(label=_('Node'), required=False, coerce=str, empty_value=None,
                                  widget=forms.Select(attrs={'class': 'narrow input-select2'}))
    template = forms.TypedChoiceField(label=_('Template'), required=False, coerce=str, empty_value=None,
                                      help_text=_('Setting template can modify lots of server attributes, '
                                                  'e.g. disks, nics.'),
                                      widget=DataSelect(attrs={'class': 'narrow input-select2'}))
    ostype = forms.TypedChoiceField(label=_('OS Type'), choices=Vm.OSTYPE, required=True, coerce=int,
                                    widget=forms.Select(attrs={'class': 'input-select2 narrow',
                                                               'required': 'required'}))
    vcpus = forms.IntegerField(label=_('VCPUs'), max_value=64, min_value=1, required=True,
                               widget=NumberInput(attrs={'class': 'input-transparent narrow', 'required': 'required'}))
    # noinspection SpellCheckingInspection
    ram = forms.IntegerField(label=_('RAM'), max_value=524288, min_value=32, required=True,
                             widget=forms.TextInput(attrs={'class': 'input-transparent narrow input-mbytes',
                                                           'required': 'required',
                                                           'pattern': '[0-9\.]+[BKMGTPEbkmgtpe]?'}))
    monitored = forms.BooleanField(label=_('Monitored?'), required=False,
                                   widget=forms.CheckboxInput(attrs={'class': 'normal-check'}))
    installed = forms.BooleanField(label=_('Installed?'), required=False,
                                   help_text=_('This field is used for informational purposes only.'),
                                   widget=forms.CheckboxInput(attrs={'class': 'normal-check'}))
    snapshot_limit_manual = forms.IntegerField(label=_('Snapshot count limit'), required=False,
                                               widget=NumberInput(attrs={'class': 'input-transparent narrow'}),
                                               help_text=_('Maximum number of manual server snapshots.'))
    snapshot_size_limit = forms.IntegerField(label=_('Snapshot size limit'), required=False,
                                             widget=NumberInput(attrs={'class': 'input-transparent narrow'}),
                                             help_text=_('Maximum size of all server snapshots.'))
    cpu_shares = forms.IntegerField(label=_('CPU Shares'), max_value=1048576, min_value=0, required=True,
                                    widget=NumberInput(attrs={'class': 'input-transparent narrow',
                                                              'required': 'required'}))
    # cpu_cap = forms.IntegerField(label=_('CPU Capping'), max_value=6400, min_value=0, required=False,
    #                             widget=NumberInput(attrs={'class': 'input-transparent narrow'}))
    zfs_io_priority = forms.IntegerField(label=_('IO Priority'), max_value=1024, min_value=0, required=True,
                                         widget=NumberInput(attrs={'class': 'input-transparent narrow',
                                                                   'required': 'required'}))
    zpool = forms.ChoiceField(label=_('Storage'), required=False,
                              widget=forms.Select(attrs={'class': 'narrow input-select2'}))
    owner = forms.ChoiceField(label=_('Owner'), required=False,
                              widget=forms.Select(attrs={'class': 'narrow input-select2'}))

    monitoring_templates = ArrayField(label=_('Monitoring templates'), required=False,
                                      help_text=_('Comma-separated list of custom monitoring templates.'),
                                      widget=ArrayWidget(attrs={'class': 'input-transparent narrow'}))
    monitoring_hostgroups = ArrayField(label=_('Monitoring hostgroups'), required=False,
                                       help_text=_('Comma-separated list of custom monitoring hostgroups.'),
                                       widget=ArrayWidget(attrs={'class': 'input-transparent narrow'}))
    mdata = DictField(label=_('Metadata'), required=False,
                      help_text=_('key=value string pairs.'),
                      widget=DictWidget(attrs={
                          'class': 'input-transparent small',
                          'rows': 5,
                          'data-raw_input_enabled': 'true',
                      }))

    def __init__(self, request, vm, *args, **kwargs):
        super(AdminServerSettingsForm, self).__init__(request, vm, *args, **kwargs)
        self.is_kvm = is_kvm(vm, self.data, prefix='opt-')
        # Set choices
        self.vm_nodes = get_nodes(request, is_compute=True)
        # TODO: node.color
        self.fields['node'].choices = [('', _('(auto)'))] + [(i.hostname, i.hostname) for i in self.vm_nodes]
        self.fields['owner'].choices = get_owners(request).values_list('username', 'username')
        self.fields['zpool'].choices = get_zpools(request).values_list('zpool', 'storage__alias').distinct()

        if not request.user.is_staff:
            self.fields['cpu_shares'].widget.attrs['disabled'] = 'disabled'
            self.fields['cpu_shares'].widget.attrs['class'] += ' uneditable-input'
            self.fields['zfs_io_priority'].widget.attrs['disabled'] = 'disabled'
            self.fields['zfs_io_priority'].widget.attrs['class'] += ' uneditable-input'

        if vm:
            empty_template_data = {}
            self.fields['ostype'].widget.attrs['disabled'] = 'disabled'
            if vm.is_deployed():
                self.fields['node'].widget.attrs['class'] += ' disable_created2'
                self.fields['zpool'].widget.attrs['class'] += ' disable_created2'
        else:
            empty_template_data = self.initial
            # Remove Linux Zone support (lx brand)
            ostype = [i for i in Vm.OSTYPE if i[0] != Vm.LINUX_ZONE]

            # Disable zone support _only_ when adding new VM (zone must be available in edit mode) - Issue #chili-461
            if not request.dc.settings.VMS_ZONE_ENABLED:
                # Remove SunOS Zone support
                ostype = [i for i in ostype if i[0] != Vm.SUNOS_ZONE]

            self.fields['ostype'].choices = ostype

        empty_template = AttrDict({'alias': _('(none)'), 'desc': '', 'web_data': empty_template_data})
        self.fields['template'].choices = [('', empty_template)] + [(i.name, i) for i in get_templates(request)]

    def _initial_data(self, request, vm):
        fix = super(AdminServerSettingsForm, self)._initial_data(request, vm)
        ret = get_vm_define(request, vm)
        # We need string representation of tags, but vm_define returns a list
        if 'tags' in ret:
            ret['tags'] = tags_to_string(ret['tags'])
        # Some serializer data need to be replaced by data expected by the parent form
        ret.update(fix)

        return ret
Beispiel #6
0
class AdminServerSettingsForm(ServerSettingsForm):
    """
    Copy of vm_define serializer (api).
    """
    admin = True
    _api_call = vm_define

    tags = TagField(
        label=_('Tags'),
        required=False,
        widget=TagWidget(attrs={'class': 'tags-select2 narrow'}),
        help_text=_('The tag will be created in case it does not exist.'))
    node = forms.TypedChoiceField(
        label=_('Node'),
        required=False,
        coerce=str,
        empty_value=None,
        widget=forms.Select(attrs={'class': 'narrow input-select2'}))
    template = forms.TypedChoiceField(
        label=_('Template'),
        required=False,
        coerce=str,
        empty_value=None,
        help_text=_('Setting template can modify lots of server attributes, '
                    'e.g. disks, nics.'),
        widget=DataSelect(attrs={'class': 'narrow input-select2'}))
    ostype = forms.TypedChoiceField(
        label=_('OS Type'),
        choices=Vm.OSTYPE,
        required=True,
        coerce=int,
        widget=forms.Select(
            attrs={
                'class': 'input-select2 narrow ostype-select',
                'required': 'required',
                'onChange': 'update_vm_form_fields_from_ostype()'
            }))
    hvm_type = forms.TypedChoiceField(
        label=_('Hypervisor Type'),
        choices=Vm.HVM_TYPE_GUI,
        required=False,
        coerce=int,
        widget=forms.Select(
            attrs={
                'class': 'input-select2 narrow hvm-type-select',
                'required': 'required',
                'onChange': 'update_vm_form_fields_from_hvm_type()'
            }))
    vcpus = forms.IntegerField(
        label=_('VCPUs'),
        required=False,
        widget=NumberInput(attrs={
            'class': 'input-transparent narrow',
            'required': 'required'
        }))
    # noinspection SpellCheckingInspection
    ram = forms.IntegerField(
        label=_('RAM'),
        required=False,
        widget=forms.TextInput(
            attrs={
                'class': 'input-transparent narrow input-mbytes',
                'required': 'required',
                'pattern': '[0-9.]+[BKMGTPEbkmgtpe]?'
            }))
    note = forms.CharField(
        label=_('Note'),
        help_text=_(
            'Text with markdown support, visible to every user with access '
            'to this server.'),
        required=False,
        widget=forms.Textarea(attrs={
            'class': 'input-transparent',
            'rows': 5
        }))
    monitored = forms.BooleanField(
        label=_('Monitored?'),
        required=False,
        widget=forms.CheckboxInput(attrs={'class': 'normal-check'}))
    installed = forms.BooleanField(
        label=_('Installed?'),
        required=False,
        help_text=_('This field is used for informational purposes only.'),
        widget=forms.CheckboxInput(attrs={'class': 'normal-check'}))
    snapshot_limit_manual = forms.IntegerField(
        label=_('Snapshot count limit'),
        required=False,
        widget=NumberInput(attrs={'class': 'input-transparent narrow'}),
        help_text=_('Maximum number of manual server snapshots.'))
    snapshot_size_percent_limit = forms.IntegerField(
        label=_('Snapshot size % limit'),
        required=False,
        widget=NumberInput(attrs={'class': 'input-transparent narrow'}),
        help_text=_(
            'Maximum size of all server snapshots as % of all disk space '
            'of this VM (example: 200% = VM with 10GB disk(s) can have '
            '20GB of snapshots).'))
    snapshot_size_limit = forms.IntegerField(
        label=_('Snapshot size limit'),
        required=False,
        widget=NumberInput(attrs={'class': 'input-transparent narrow'}),
        help_text=_('Maximum size of all server snapshots. '
                    'If set, it takes precedence over % limit.'))
    cpu_shares = forms.IntegerField(
        label=_('CPU Shares'),
        max_value=1048576,
        min_value=0,
        required=True,
        widget=NumberInput(attrs={
            'class': 'input-transparent narrow',
            'required': 'required'
        }))
    # cpu_cap = forms.IntegerField(label=_('CPU Capping'), max_value=6400, min_value=0, required=False,
    #                             widget=NumberInput(attrs={'class': 'input-transparent narrow'}))
    zfs_io_priority = forms.IntegerField(
        label=_('IO Priority'),
        max_value=1024,
        min_value=0,
        required=True,
        widget=NumberInput(attrs={
            'class': 'input-transparent narrow',
            'required': 'required'
        }))
    bootrom = forms.ChoiceField(
        label=_('Bootrom'),
        required=False,
        widget=forms.Select(attrs={'class': 'narrow input-select2'}))
    zpool = forms.ChoiceField(
        label=_('Storage'),
        required=False,
        widget=forms.Select(attrs={'class': 'narrow input-select2'}))
    owner = forms.ChoiceField(
        label=_('Owner'),
        required=False,
        widget=forms.Select(attrs={'class': 'narrow input-select2'}))

    monitoring_templates = ArrayField(
        label=_('Monitoring templates'),
        required=False,
        tags=True,
        help_text=_('Comma-separated list of custom monitoring templates.'),
        widget=ArrayWidget(tags=True,
                           escape_space=False,
                           attrs={'class': 'tags-select2 narrow'}))
    monitoring_hostgroups = ArrayField(
        label=_('Monitoring hostgroups'),
        required=False,
        tags=True,
        validators=[
            RegexValidator(regex=MonitoringBackend.RE_MONITORING_HOSTGROUPS)
        ],
        help_text=_('Comma-separated list of custom monitoring hostgroups.'),
        widget=ArrayWidget(tags=True,
                           escape_space=False,
                           attrs={'class': 'tags-select2 narrow'}))
    mdata = DictField(label=_('Metadata'),
                      required=False,
                      help_text=_('key=value string pairs.'),
                      widget=DictWidget(
                          attrs={
                              'class': 'input-transparent small',
                              'rows': 5,
                              'data-raw_input_enabled': 'true',
                          }))

    def __init__(self, request, vm, *args, **kwargs):
        super(AdminServerSettingsForm, self).__init__(request, vm, *args,
                                                      **kwargs)
        dc_settings = request.dc.settings
        # Set choices
        self.vm_nodes = get_nodes(request, is_compute=True)
        # TODO: node.color
        self.fields['node'].choices = [('', _('(auto)'))] + [
            (i.hostname, i.hostname) for i in self.vm_nodes
        ]
        self.fields['owner'].choices = get_owners(request).values_list(
            'username', 'username')
        self.fields['zpool'].choices = get_zpools(request).values_list(
            'zpool', 'storage__alias').distinct()
        self.fields['bootrom'].choices = Vm.BHYVE_BOOTROM

        if not request.user.is_staff:
            self.fields['cpu_shares'].widget.attrs['disabled'] = 'disabled'
            self.fields['cpu_shares'].widget.attrs[
                'class'] += ' uneditable-input'
            self.fields['zfs_io_priority'].widget.attrs[
                'disabled'] = 'disabled'
            self.fields['zfs_io_priority'].widget.attrs[
                'class'] += ' uneditable-input'

        if dc_settings.MON_ZABBIX_TEMPLATES_VM_RESTRICT:
            self.fields[
                'monitoring_templates'].widget.tag_choices = dc_settings.MON_ZABBIX_TEMPLATES_VM_ALLOWED

        if dc_settings.MON_ZABBIX_HOSTGROUPS_VM_RESTRICT:
            self.fields[
                'monitoring_hostgroups'].widget.tag_choices = dc_settings.MON_ZABBIX_HOSTGROUPS_VM_ALLOWED

        if dc_settings.MON_ZABBIX_HOSTGROUPS_VM:
            self.fields['monitoring_hostgroups'].help_text += _(' Automatically added hostgroups: ') \
                                                              + ', '.join(dc_settings.MON_ZABBIX_HOSTGROUPS_VM)

        if vm:
            empty_template_data = {}
            self.fields['ostype'].widget.attrs['disabled'] = 'disabled'
            self.fields['hvm_type'].widget.attrs['disabled'] = 'disabled'
            if not vm.is_hvm():
                # for zones the only HVM choice is NO hypervisor
                self.fields['hvm_type'].choices = Vm.HVM_TYPE_GUI_NO_HYPERVISOR

            if vm.is_deployed():
                self.fields['node'].widget.attrs[
                    'class'] += ' disable_created2'
                self.fields['zpool'].widget.attrs[
                    'class'] += ' disable_created2'
        else:
            empty_template_data = self.initial
            ostype = Vm.OSTYPE

            # Disable zone support _only_ when adding new VM (zone must be available in edit mode) - Issue #chili-461
            if not dc_settings.VMS_ZONE_ENABLED:
                # Remove SunOS Zone support
                ostype = [i for i in ostype if i[0] not in Vm.ZONE_OSTYPES]

            self.fields['ostype'].choices = ostype

        empty_template = AttrDict({
            'alias': _('(none)'),
            'desc': '',
            'web_data': empty_template_data
        })
        self.fields['template'].choices = [('', empty_template)] + [
            (i.name, i) for i in get_templates(request)
        ]

    def _initial_data(self, request, vm):
        fix = super(AdminServerSettingsForm, self)._initial_data(request, vm)
        ret = get_vm_define(request, vm)
        # We need string representation of tags, but vm_define returns a list
        if 'tags' in ret:
            ret['tags'] = tags_to_string(ret['tags'])
        # Some serializer data need to be replaced by data expected by the parent form
        ret.update(fix)

        return ret
Beispiel #7
0
class DcNodeForm(SerializerForm):
    """
    Create or update Dc<->Node association by calling dc_node.
    """
    _api_call = dc_node

    hostname = forms.ChoiceField(
        label=_('Hostname'),
        required=True,
        widget=forms.Select(
            attrs={'class': 'narrow input-select2 disable_created2'}))
    strategy = forms.TypedChoiceField(
        label=_('Resource strategy'),
        required=True,
        coerce=int,
        choices=DcNode.STRATEGY,
        widget=forms.Select(attrs={'class': 'input-select2 narrow'}))
    priority = forms.IntegerField(
        label=_('Priority'),
        required=True,
        help_text=_('Higher priority means that the automatic node chooser '
                    'will more likely choose this node.'),
        widget=NumberInput(attrs={
            'class': 'input-transparent narrow',
            'required': 'required'
        }))
    cpu = forms.IntegerField(
        label=_('CPUs'),
        required=False,
        help_text=_('Total number of vCPUs for VMs.'),
        widget=NumberInput(attrs={
            'class': 'input-transparent narrow',
            'required': 'required'
        }))
    # noinspection SpellCheckingInspection
    ram = forms.IntegerField(
        label=_('RAM'),
        required=False,
        help_text=_('Total RAM size in MB for VMs.'),
        widget=forms.TextInput(
            attrs={
                'class': 'input-transparent narrow input-mbytes',
                'required': 'required',
                'pattern': '[0-9\.]+[BKMGTPEbkmgtpe]?'
            }))
    # noinspection SpellCheckingInspection
    disk = forms.IntegerField(
        label=_('Disk pool size'),
        required=False,
        help_text=_('Size of the local disk pool for VMs.'),
        widget=forms.TextInput(
            attrs={
                'class': 'input-transparent narrow input-mbytes',
                'required': 'required',
                'pattern': '[0-9\.]+[BKMGTPEbkmgtpe]?'
            }))
    add_storage = forms.TypedChoiceField(
        label=_('Attach node Storages'),
        required=False,
        coerce=int,
        empty_value=0,
        choices=add_storage_choices,
        widget=forms.Select(
            attrs={'class': 'narrow input-select2 disable_created2'}))

    def __init__(self, request, instance, *args, **kwargs):
        super(DcNodeForm, self).__init__(request, instance, *args, **kwargs)
        self.fields['hostname'].choices = Node.objects.all().values_list(
            'hostname', 'hostname')

    def _initial_data(self, request, obj):
        return obj.web_data

    def _has_changed(self):
        try:
            del self.cleaned_data['add_storage']
        except KeyError:
            pass
        return super(DcNodeForm, self)._has_changed()