Merge pull request #398 from mwhudson/md-name-validation
validate raid and vg/lv names
This commit is contained in:
commit
583909a716
|
@ -14,6 +14,8 @@
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
from urwid import (
|
from urwid import (
|
||||||
connect_signal,
|
connect_signal,
|
||||||
|
@ -25,7 +27,11 @@ from subiquitycore.ui.container import (
|
||||||
)
|
)
|
||||||
from subiquitycore.ui.form import (
|
from subiquitycore.ui.form import (
|
||||||
ReadOnlyField,
|
ReadOnlyField,
|
||||||
StringField,
|
simple_field,
|
||||||
|
WantsToKnowFormField,
|
||||||
|
)
|
||||||
|
from subiquitycore.ui.interactive import (
|
||||||
|
StringEditor,
|
||||||
)
|
)
|
||||||
from subiquitycore.ui.stretchy import (
|
from subiquitycore.ui.stretchy import (
|
||||||
Stretchy,
|
Stretchy,
|
||||||
|
@ -44,13 +50,33 @@ from subiquity.ui.views.filesystem.compound import (
|
||||||
log = logging.getLogger('subiquity.ui.lvm')
|
log = logging.getLogger('subiquity.ui.lvm')
|
||||||
|
|
||||||
|
|
||||||
|
class VGNameEditor(StringEditor, WantsToKnowFormField):
|
||||||
|
def __init__(self):
|
||||||
|
self.valid_char_pat = r'[-a-zA-Z0-9_+.]'
|
||||||
|
self.error_invalid_char = _("The only characters permitted in the "
|
||||||
|
"name of a volume group are a-z, A-Z, "
|
||||||
|
"0-9, +, _, . and -")
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
def valid_char(self, ch):
|
||||||
|
if len(ch) == 1 and not re.match(self.valid_char_pat, ch):
|
||||||
|
self.bff.in_error = True
|
||||||
|
self.bff.show_extra(("info_error", self.error_invalid_char))
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return super().valid_char(ch)
|
||||||
|
|
||||||
|
|
||||||
|
VGNameField = simple_field(VGNameEditor)
|
||||||
|
|
||||||
|
|
||||||
class VolGroupForm(CompoundDiskForm):
|
class VolGroupForm(CompoundDiskForm):
|
||||||
|
|
||||||
def __init__(self, model, possible_components, initial, vg_names):
|
def __init__(self, model, possible_components, initial, vg_names):
|
||||||
self.vg_names = vg_names
|
self.vg_names = vg_names
|
||||||
super().__init__(model, possible_components, initial)
|
super().__init__(model, possible_components, initial)
|
||||||
|
|
||||||
name = StringField(_("Name:"))
|
name = VGNameField(_("Name:"))
|
||||||
devices = MultiDeviceField(_("Devices:"))
|
devices = MultiDeviceField(_("Devices:"))
|
||||||
size = ReadOnlyField(_("Size:"))
|
size = ReadOnlyField(_("Size:"))
|
||||||
|
|
||||||
|
@ -60,7 +86,15 @@ class VolGroupForm(CompoundDiskForm):
|
||||||
"group.")
|
"group.")
|
||||||
|
|
||||||
def validate_name(self):
|
def validate_name(self):
|
||||||
if self.name.value in self.vg_names:
|
v = self.name.value
|
||||||
|
if not v:
|
||||||
|
return _("The name of a volume group cannot be empty")
|
||||||
|
if v.startswith('-'):
|
||||||
|
return _("The name of a volume group cannot start with a hyphen")
|
||||||
|
if v in ('.', '..', 'md') or os.path.exists('/dev/' + v):
|
||||||
|
return _("{} is not a valid name for a volume group").format(
|
||||||
|
v)
|
||||||
|
if v in self.vg_names:
|
||||||
return _("There is already a volume group named '{}'").format(
|
return _("There is already a volume group named '{}'").format(
|
||||||
self.name.value)
|
self.name.value)
|
||||||
|
|
||||||
|
|
|
@ -20,13 +20,15 @@ configuration.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
import re
|
||||||
|
|
||||||
from urwid import connect_signal, Text
|
from urwid import connect_signal, Text
|
||||||
|
|
||||||
from subiquitycore.ui.form import (
|
from subiquitycore.ui.form import (
|
||||||
Form,
|
Form,
|
||||||
FormField,
|
FormField,
|
||||||
StringField,
|
simple_field,
|
||||||
|
WantsToKnowFormField,
|
||||||
)
|
)
|
||||||
from subiquitycore.ui.interactive import StringEditor
|
from subiquitycore.ui.interactive import StringEditor
|
||||||
from subiquitycore.ui.selector import Option, Selector
|
from subiquitycore.ui.selector import Option, Selector
|
||||||
|
@ -92,6 +94,26 @@ class SizeField(FormField):
|
||||||
return SizeWidget(form)
|
return SizeWidget(form)
|
||||||
|
|
||||||
|
|
||||||
|
class LVNameEditor(StringEditor, WantsToKnowFormField):
|
||||||
|
def __init__(self):
|
||||||
|
self.valid_char_pat = r'[-a-zA-Z0-9_+.]'
|
||||||
|
self.error_invalid_char = _("The only characters permitted in the "
|
||||||
|
"name of a logical volume are a-z, A-Z, "
|
||||||
|
"0-9, +, _, . and -")
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
def valid_char(self, ch):
|
||||||
|
if len(ch) == 1 and not re.match(self.valid_char_pat, ch):
|
||||||
|
self.bff.in_error = True
|
||||||
|
self.bff.show_extra(("info_error", self.error_invalid_char))
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return super().valid_char(ch)
|
||||||
|
|
||||||
|
|
||||||
|
LVNameField = simple_field(LVNameEditor)
|
||||||
|
|
||||||
|
|
||||||
class PartitionForm(Form):
|
class PartitionForm(Form):
|
||||||
|
|
||||||
def __init__(self, mountpoints, max_size, initial, ok_for_slash_boot,
|
def __init__(self, mountpoints, max_size, initial, ok_for_slash_boot,
|
||||||
|
@ -112,7 +134,7 @@ class PartitionForm(Form):
|
||||||
def select_fstype(self, sender, fs):
|
def select_fstype(self, sender, fs):
|
||||||
self.mount.enabled = fs.is_mounted
|
self.mount.enabled = fs.is_mounted
|
||||||
|
|
||||||
name = StringField(_("Name: "))
|
name = LVNameField(_("Name: "))
|
||||||
size = SizeField()
|
size = SizeField()
|
||||||
fstype = FSTypeField(_("Format:"))
|
fstype = FSTypeField(_("Format:"))
|
||||||
mount = MountField(_("Mount:"))
|
mount = MountField(_("Mount:"))
|
||||||
|
@ -135,8 +157,20 @@ class PartitionForm(Form):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def validate_name(self):
|
def validate_name(self):
|
||||||
log.debug("validate_name %s %s", self.name.value, self.lvm_names)
|
v = self.name.value
|
||||||
if self.name.value in self.lvm_names:
|
if not v:
|
||||||
|
return _("The name of a logical volume cannot be empty")
|
||||||
|
if v.startswith('-'):
|
||||||
|
return _("The name of a logical volume cannot start with a hyphen")
|
||||||
|
if v in ('.', '..', 'snapshot', 'pvmove'):
|
||||||
|
return _("A logical volume may not be called {}").format(v)
|
||||||
|
for substring in ['_cdata', '_cmeta', '_corig', '_mlog', '_mimage',
|
||||||
|
'_pmspare', '_rimage', '_rmeta', '_tdata',
|
||||||
|
'_tmeta', '_vorigin']:
|
||||||
|
if substring in v:
|
||||||
|
return _('The name of a logical volume may not contain '
|
||||||
|
'"{}"').format(substring)
|
||||||
|
if v in self.lvm_names:
|
||||||
return _("There is already a logical volume named {}.").format(
|
return _("There is already a logical volume named {}.").format(
|
||||||
self.name.value)
|
self.name.value)
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,11 @@ from subiquitycore.ui.container import (
|
||||||
from subiquitycore.ui.form import (
|
from subiquitycore.ui.form import (
|
||||||
ChoiceField,
|
ChoiceField,
|
||||||
ReadOnlyField,
|
ReadOnlyField,
|
||||||
StringField,
|
simple_field,
|
||||||
|
WantsToKnowFormField,
|
||||||
|
)
|
||||||
|
from subiquitycore.ui.interactive import (
|
||||||
|
StringEditor,
|
||||||
)
|
)
|
||||||
from subiquitycore.ui.selector import (
|
from subiquitycore.ui.selector import (
|
||||||
Option,
|
Option,
|
||||||
|
@ -55,18 +59,41 @@ raidlevel_choices = [
|
||||||
Option((_(level.name), True, level)) for level in raidlevels]
|
Option((_(level.name), True, level)) for level in raidlevels]
|
||||||
|
|
||||||
|
|
||||||
|
class RaidnameEditor(StringEditor, WantsToKnowFormField):
|
||||||
|
def valid_char(self, ch):
|
||||||
|
if len(ch) == 1 and ch == '/':
|
||||||
|
self.bff.in_error = True
|
||||||
|
self.bff.show_extra(("info_error",
|
||||||
|
_("/ is not permitted "
|
||||||
|
"in the name of a RAID device")))
|
||||||
|
return False
|
||||||
|
elif len(ch) == 1 and ch.isspace():
|
||||||
|
self.bff.in_error = True
|
||||||
|
self.bff.show_extra(("info_error",
|
||||||
|
_("Whitespace is not permitted in the "
|
||||||
|
"name of a RAID device")))
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return super().valid_char(ch)
|
||||||
|
|
||||||
|
|
||||||
|
RaidnameField = simple_field(RaidnameEditor)
|
||||||
|
|
||||||
|
|
||||||
class RaidForm(CompoundDiskForm):
|
class RaidForm(CompoundDiskForm):
|
||||||
|
|
||||||
def __init__(self, model, possible_components, initial, raid_names):
|
def __init__(self, model, possible_components, initial, raid_names):
|
||||||
self.raid_names = raid_names
|
self.raid_names = raid_names
|
||||||
super().__init__(model, possible_components, initial)
|
super().__init__(model, possible_components, initial)
|
||||||
|
|
||||||
name = StringField(_("Name:"))
|
name = RaidnameField(_("Name:"))
|
||||||
level = ChoiceField(_("RAID Level:"), choices=raidlevel_choices)
|
level = ChoiceField(_("RAID Level:"), choices=raidlevel_choices)
|
||||||
devices = MultiDeviceField(_("Devices:"))
|
devices = MultiDeviceField(_("Devices:"))
|
||||||
size = ReadOnlyField(_("Size:"))
|
size = ReadOnlyField(_("Size:"))
|
||||||
|
|
||||||
def clean_name(self, val):
|
def clean_name(self, val):
|
||||||
|
if not val:
|
||||||
|
raise ValueError("The name cannot be empty")
|
||||||
if not re.match('md[0-9]+', val):
|
if not re.match('md[0-9]+', val):
|
||||||
val = 'md/' + val
|
val = 'md/' + val
|
||||||
return val
|
return val
|
||||||
|
@ -75,6 +102,8 @@ class RaidForm(CompoundDiskForm):
|
||||||
if self.name.value in self.raid_names:
|
if self.name.value in self.raid_names:
|
||||||
return _("There is already a RAID named '{}'").format(
|
return _("There is already a RAID named '{}'").format(
|
||||||
self.name.value)
|
self.name.value)
|
||||||
|
if self.name.value in ('/dev/md/.', '/dev/md/..'):
|
||||||
|
return _(". and .. are not valid names for RAID devices")
|
||||||
|
|
||||||
def validate_devices(self):
|
def validate_devices(self):
|
||||||
log.debug(
|
log.debug(
|
||||||
|
|
Loading…
Reference in New Issue