コード例 #1
0
ファイル: forms.py プロジェクト: vanloswang/freenas
class LAGGInterfaceForm(ModelForm):
    lagg_interfaces = forms.MultipleChoiceField(
        widget=forms.SelectMultiple(),
        label=_('Physical NICs in the LAGG'),
    )

    class Meta:
        model = models.LAGGInterface
        exclude = ('lagg_interface', )
        widgets = {
            'lagg_protocol': forms.RadioSelect(),
        }

    def __init__(self, *args, **kwargs):
        super(LAGGInterfaceForm, self).__init__(*args, **kwargs)
        self.fields['lagg_interfaces'].choices = list(
            choices.NICChoices(nolagg=True))
        # Remove empty option (e.g. -------)
        self.fields['lagg_protocol'].choices = (
            self.fields['lagg_protocol'].choices[1:])

    def save(self, *args, **kwargs):

        # Search for a available slot for laggX interface
        interface_names = [
            v[0] for v in models.Interfaces.objects.all().values_list(
                'int_interface')
        ]
        candidate_index = 0
        while ("lagg%d" % (candidate_index)) in interface_names:
            candidate_index += 1
        lagg_name = "lagg%d" % candidate_index
        lagg_protocol = self.cleaned_data['lagg_protocol']
        lagg_member_list = self.cleaned_data['lagg_interfaces']
        with transaction.atomic():
            # Step 1: Create an entry in interface table that
            # represents the lagg interface
            lagg_interface = models.Interfaces(int_interface=lagg_name,
                                               int_name=lagg_name,
                                               int_dhcp=False,
                                               int_ipv6auto=False)
            lagg_interface.save()
            # Step 2: Write associated lagg attributes
            lagg_interfacegroup = models.LAGGInterface(
                lagg_interface=lagg_interface, lagg_protocol=lagg_protocol)
            lagg_interfacegroup.save()
            # Step 3: Write lagg's members in the right order
            order = 0
            for interface in lagg_member_list:
                lagg_member_entry = models.LAGGInterfaceMembers(
                    lagg_interfacegroup=lagg_interfacegroup,
                    lagg_ordernum=order,
                    lagg_physnic=interface,
                    lagg_deviceoptions='up')
                lagg_member_entry.save()
                order = order + 1
        self.instance = lagg_interfacegroup
        notifier().start("network")
        return lagg_interfacegroup
コード例 #2
0
ファイル: forms.py プロジェクト: BillTheBest/MetaNAS
class LAGGInterfaceForm(forms.Form):
    lagg_protocol = forms.ChoiceField(choices=choices.LAGGType,
                          widget=forms.RadioSelect())
    lagg_interfaces = forms.MultipleChoiceField(
                            widget=forms.SelectMultiple(),
                            label=_('Physical NICs in the LAGG')
                            )

    def __init__(self, *args, **kwargs):
        super(LAGGInterfaceForm, self).__init__(*args, **kwargs)
        self.fields['lagg_interfaces'].choices = list(
            choices.NICChoices(nolagg=True)
            )
コード例 #3
0
class LAGGInterfaceForm(ModelForm):
    lagg_interfaces = forms.MultipleChoiceField(
        widget=forms.SelectMultiple(),
        label=_('Physical NICs in the LAGG'),
    )

    class Meta:
        model = models.LAGGInterface
        exclude = ('lagg_interface', )
        widgets = {
            'lagg_protocol': forms.RadioSelect(),
        }

    def __init__(self, *args, **kwargs):
        super(LAGGInterfaceForm, self).__init__(*args, **kwargs)
        self.fields['lagg_interfaces'].choices = list(
            choices.NICChoices(nolagg=True))

        # For HA we dont want people using failover type
        # See #25351
        if not notifier().is_freenas() and notifier().failover_licensed():
            filter_failover_type = True
        else:
            filter_failover_type = False
        # Remove empty option (e.g. -------)
        lagg_protocol = list(self.fields['lagg_protocol'].choices[1:])
        if filter_failover_type:
            lagg_protocol = filter(lambda x: x[0] != 'failover', lagg_protocol)
        self.fields['lagg_protocol'].choices = tuple(lagg_protocol)

    def save(self, *args, **kwargs):

        # Search for a available slot for laggX interface
        interface_names = [
            v[0] for v in models.Interfaces.objects.all().values_list(
                'int_interface')
        ]
        candidate_index = 0
        while ("lagg%d" % (candidate_index)) in interface_names:
            candidate_index += 1
        lagg_name = "lagg%d" % candidate_index
        lagg_protocol = self.cleaned_data['lagg_protocol']
        lagg_member_list = self.cleaned_data['lagg_interfaces']
        with DBSync():
            model_objs = []
            try:
                # Step 1: Create an entry in interface table that
                # represents the lagg interface
                lagg_interface = models.Interfaces(int_interface=lagg_name,
                                                   int_name=lagg_name,
                                                   int_dhcp=False,
                                                   int_ipv6auto=False)
                lagg_interface.save()
                model_objs.append(lagg_interface)
                # Step 2: Write associated lagg attributes
                lagg_interfacegroup = models.LAGGInterface(
                    lagg_interface=lagg_interface, lagg_protocol=lagg_protocol)
                lagg_interfacegroup.save()
                model_objs.append(lagg_interfacegroup)
                # Step 3: Write lagg's members in the right order
                order = 0
                for interface in lagg_member_list:
                    lagg_member_entry = models.LAGGInterfaceMembers(
                        lagg_interfacegroup=lagg_interfacegroup,
                        lagg_ordernum=order,
                        lagg_physnic=interface,
                    )
                    model_objs.append(
                        models.Interfaces.objects.create(
                            int_interface=interface,
                            int_name=f'member of {lagg_name}',
                        ))
                    lagg_member_entry.save()
                    model_objs.append(lagg_member_entry)
                    order = order + 1
            except Exception:
                for obj in reversed(model_objs):
                    obj.delete()
                raise
        self.instance = lagg_interfacegroup
        return lagg_interfacegroup

    def delete(self, *args, **kwargs):
        with DBSync():
            super(LAGGInterfaceForm, self).delete(*args, **kwargs)
        notifier().start("network")

    def done(self, *args, **kwargs):
        super(LAGGInterfaceForm, self).done(*args, **kwargs)
        notifier().start("network")
コード例 #4
0
ファイル: forms.py プロジェクト: BillTheBest/MetaNAS
class VolumeWizardForm(forms.Form):
    volume_name = forms.CharField(max_length=30,
                                  label=_('Volume name'),
                                  required=False)
    volume_fstype = forms.ChoiceField(
        choices=((x, x) for x in ('UFS', 'ZFS')),
        widget=forms.RadioSelect(attrs=attrs_dict),
        label=_('Filesystem type'))
    volume_disks = forms.MultipleChoiceField(
        choices=(),
        widget=forms.SelectMultiple(attrs=attrs_dict),
        label='Member disks',
        required=False)
    group_type = forms.ChoiceField(choices=(),
                                   widget=forms.RadioSelect(attrs=attrs_dict),
                                   required=False)
    force4khack = forms.BooleanField(
        required=False,
        initial=False,
        help_text=_('Force 4096 bytes sector size'))
    ufspathen = forms.BooleanField(initial=False,
                                   label=_('Specify custom path'),
                                   required=False)
    ufspath = forms.CharField(max_length=1024, label=_('Path'), required=False)

    def __init__(self, *args, **kwargs):
        super(VolumeWizardForm, self).__init__(*args, **kwargs)
        self.fields['volume_disks'].choices = self._populate_disk_choices()
        qs = models.Volume.objects.filter(vol_fstype='ZFS')
        if qs.exists():
            self.fields['volume_add'] = forms.ChoiceField(
                label=_('Volume add'), required=False)
            self.fields['volume_add'].choices = [('', '-----')] + \
                                        [(x.vol_name, x.vol_name) for x in qs]
            self.fields['volume_add'].widget.attrs['onChange'] = (
                'wizardcheckings(true);')
        self.fields['volume_fstype'].widget.attrs['onClick'] = (
            'wizardcheckings();')
        self.fields['ufspathen'].widget.attrs['onClick'] = (
            'toggleGeneric("id_ufspathen", ["id_ufspath"], true);')
        if not self.data.get("ufspathen", False):
            self.fields['ufspath'].widget.attrs['disabled'] = 'disabled'
        self.fields['ufspath'].widget.attrs['promptMessage'] = _(
            "Leaving this"
            " blank will give the volume a default path of "
            "/mnt/${VOLUME_NAME}")

        grouptype_choices = (
            ('mirror', 'mirror'),
            ('stripe', 'stripe'),
        )
        fstype = self.data.get("volume_fstype", None)
        if "volume_disks" in self.data:
            disks = self.data.getlist("volume_disks")
        else:
            disks = []
        if fstype == "UFS":
            l = len(disks) - 1
            if l >= 2 and (((l - 1) & l) == 0):
                grouptype_choices += (('raid3', 'RAID-3'), )
        elif fstype == "ZFS":
            if len(disks) >= 3:
                grouptype_choices += (('raidz', 'RAID-Z'), )
            if len(disks) >= 4:
                grouptype_choices += (('raidz2', 'RAID-Z2'), )
            # Not yet
            #if len(disks) >= 5:
            #    grouptype_choices += ( ('raidz3', 'RAID-Z3'), )
        self.fields['group_type'].choices = grouptype_choices

    def _populate_disk_choices(self):

        disks = []

        # Grab disk list
        # Root device already ruled out
        for disk, info in notifier().get_disks().items():
            disks.append(
                Disk(info['devname'],
                     info['capacity'],
                     serial=info.get('ident')))

        # Exclude what's already added
        used_disks = []
        for v in models.Volume.objects.all():
            used_disks.extend(v.get_disks())

        qs = iSCSITargetExtent.objects.filter(iscsi_target_extent_type='Disk')
        used_disks.extend([i.get_device()[5:] for i in qs])

        for d in list(disks):
            if d.dev in used_disks:
                disks.remove(d)

        choices = sorted(disks)
        choices = [tuple(d) for d in choices]
        return choices

    def clean_volume_name(self):
        vname = self.cleaned_data['volume_name']
        if vname and not re.search(r'^[a-z][-_.a-z0-9]*$', vname, re.I):
            raise forms.ValidationError(
                _("The volume name must start with "
                  "letters and may include numbers, \"-\", \"_\" and \".\" ."))
        if models.Volume.objects.filter(vol_name=vname).exists():
            raise forms.ValidationError(
                _("A volume with that name already "
                  "exists."))
        return vname

    def clean_group_type(self):
        if 'volume_disks' not in self.cleaned_data or \
                len(self.cleaned_data['volume_disks']) > 1 and \
                self.cleaned_data['group_type'] in (None, ''):
            raise forms.ValidationError(_("This field is required."))
        return self.cleaned_data['group_type']

    def clean_ufspath(self):
        ufspath = self.cleaned_data['ufspath']
        if not ufspath:
            return None
        if not access(ufspath, 0):
            raise forms.ValidationError(_("Path does not exist."))
        st = stat(ufspath)
        if not S_ISDIR(st.st_mode):
            raise forms.ValidationError(_("Path is not a directory."))
        return ufspath

    def clean(self):
        cleaned_data = self.cleaned_data
        volume_name = cleaned_data.get("volume_name", "")
        disks = cleaned_data.get("volume_disks")
        if volume_name and cleaned_data.get("volume_add"):
            self._errors['__all__'] = self.error_class([
                _("You cannot select an existing ZFS volume and specify a new "
                  "volume name"),
            ])
        elif not (volume_name or cleaned_data.get("volume_add")):
            self._errors['__all__'] = self.error_class([
                _("You must specify a new volume name or select an existing "
                  "ZFS volume to append a virtual device"),
            ])
        elif not volume_name:
            volume_name = cleaned_data.get("volume_add")

        if cleaned_data.get("volume_fstype") not in ('ZFS', 'UFS'):
            msg = _(u"You must select a filesystem")
            self._errors["volume_fstype"] = self.error_class([msg])
            cleaned_data.pop("volume_fstype", None)
        if len(disks) == 0 and models.Volume.objects.filter(
                vol_name=volume_name).count() == 0:
            msg = _(u"This field is required")
            self._errors["volume_disks"] = self.error_class([msg])
            del cleaned_data["volume_disks"]
        if (cleaned_data.get("volume_fstype") == 'ZFS' and \
                models.Volume.objects.filter(vol_name=volume_name).exclude(
                    vol_fstype='ZFS').count() > 0
                ) or (
                    cleaned_data.get("volume_fstype") == 'UFS' and \
                    models.Volume.objects.filter(
                        vol_name=volume_name).count() > 0
                ):
            msg = _(u"You already have a volume with same name")
            self._errors["volume_name"] = self.error_class([msg])
            del cleaned_data["volume_name"]

        if cleaned_data.get("volume_fstype", None) == 'ZFS':
            if volume_name in ('log', ):
                msg = _(u"\"log\" is a reserved word and thus cannot be used")
                self._errors["volume_name"] = self.error_class([msg])
                cleaned_data.pop("volume_name", None)
            elif re.search(r'^c[0-9].*', volume_name) or \
                    re.search(r'^mirror.*', volume_name) or \
                    re.search(r'^spare.*', volume_name) or \
                    re.search(r'^raidz.*', volume_name):
                msg = _(u"The volume name may NOT start with c[0-9], mirror, "
                        "raidz or spare")
                self._errors["volume_name"] = self.error_class([msg])
                cleaned_data.pop("volume_name", None)
        elif cleaned_data.get("volume_fstype") == 'UFS' and volume_name:
            if len(volume_name) > 9:
                msg = _(u"UFS volume names cannot be higher than 9 characters")
                self._errors["volume_name"] = self.error_class([msg])
                cleaned_data.pop("volume_name", None)
            elif not re.search(r'^[a-z0-9]+$', volume_name, re.I):
                msg = _(u"UFS volume names can only contain alphanumeric "
                        "characters")
                self._errors["volume_name"] = self.error_class([msg])
                cleaned_data.pop("volume_name", None)

        return cleaned_data

    def done(self, request):
        # Construct and fill forms into database.
        volume_name = self.cleaned_data.get("volume_name") or \
                            self.cleaned_data.get("volume_add")
        volume_fstype = self.cleaned_data['volume_fstype']
        disk_list = self.cleaned_data['volume_disks']
        force4khack = self.cleaned_data.get("force4khack", False)
        ufspath = self.cleaned_data['ufspath']
        mp_options = "rw"
        mp_path = None

        if (len(disk_list) < 2):
            if volume_fstype == 'ZFS':
                group_type = 'stripe'
            else:
                # UFS middleware expects no group_type for single disk volume
                group_type = ''
        else:
            group_type = self.cleaned_data['group_type']

        with transaction.commit_on_success():
            vols = models.Volume.objects.filter(vol_name=volume_name,
                                                vol_fstype='ZFS')
            if vols.count() == 1:
                volume = vols[0]
                add = True
            else:
                add = False
                volume = models.Volume(vol_name=volume_name,
                                       vol_fstype=volume_fstype)
                volume.save()

                mp_path = ufspath if ufspath else '/mnt/' + volume_name

                if volume_fstype == 'UFS':
                    mp_options = 'rw,nfsv4acls'

                mp = models.MountPoint(mp_volume=volume,
                                       mp_path=mp_path,
                                       mp_options=mp_options)
                mp.save()
            self.volume = volume

            zpoolfields = re.compile(r'zpool_(.+)')
            grouped = OrderedDict()
            grouped['root'] = {'type': group_type, 'disks': disk_list}
            for i, gtype in request.POST.items():
                if zpoolfields.match(i):
                    if gtype == 'none':
                        continue
                    disk = zpoolfields.search(i).group(1)
                    if gtype in grouped:
                        # if this is a log vdev we need to mirror it for safety
                        if gtype == 'log':
                            grouped[gtype]['type'] = 'log mirror'
                        grouped[gtype]['disks'].append(disk)
                    else:
                        grouped[gtype] = {
                            'type': gtype,
                            'disks': [
                                disk,
                            ]
                        }

            if len(disk_list) > 0 and add:
                notifier().zfs_volume_attach_group(volume,
                                                   grouped['root'],
                                                   force4khack=force4khack)

            if add:
                for grp_type in grouped:
                    if grp_type in ('log', 'cache', 'spare'):
                        notifier().zfs_volume_attach_group(
                            volume,
                            grouped.get(grp_type),
                            force4khack=force4khack)

            else:
                notifier().init("volume",
                                volume,
                                groups=grouped,
                                force4khack=force4khack,
                                path=ufspath)
                if volume.vol_fstype == 'ZFS':
                    models.Scrub.objects.create(scrub_volume=volume)
                    try:
                        notifier().restart("cron")
                    except:
                        pass

        if mp_path in ('/etc', '/var', '/usr'):
            device = '/dev/ufs/' + volume_name
            mp = '/mnt/' + volume_name

            if not access(mp, 0):
                mkdir(mp, 755)

            mount(device, mp)
            popen("/usr/local/bin/rsync -avz '%s/*' '%s/'" %
                  (mp_path, mp)).close()
            umount(mp)

            if access(mp, 0):
                rmdir(mp)

        else:

            # This must be outside transaction block to make sure the changes
            # are committed before the call of mx-fstab
            notifier().reload("disk")