import os import re import string FS_TYPES = ['xfs', 'ext4', 'ext3', 'ext2', 'vfat', 'swap'] SPECIAL_FS_TYPES = ['tmpfs', 'none'] FS_OPTIONS = ['async', 'atime', 'noatime', 'auto', 'noauto', 'defaults', 'dev', 'nodev', 'diratime', 'nodiratime', 'dirsync', 'exec', 'noexec', 'group', 'iversion', 'noiversion', 'mand', 'nomand', '_netdev', 'nofail', 'relatime', 'norelatime', 'strictatime', 'nostrictatime', 'lazytime', 'nolazytime', 'suid', 'nosuid', 'silent', 'loud', 'owner', 'remount', 'ro', 'rw', 'sync', 'user', 'users'] # XXX quick hack for some XFS options EXTRA_FS_OPTIONS = ['noquota', 'uquota', 'usrquota', 'quota', 'uqnoenforce', 'qnoenforce', 'gquota', 'grpquota', 'gqnoenforce', 'pquota', 'prjquota', 'pqnoenforce'] IPADDR_RE = '^' + '\.'.join(['[0-9]{1,3}'] * 4) + '$' MACADDR_RE = '^' + ':'.join(['[0-9a-fA-F]{2}'] * 6) + '$' def str_is_bool(s): "validate the given string as a boolean" if s.lower() in ["0", "false", "no", "off", "1", "true", "yes", "on"]: return True else: return False def str_to_bool(s): "convert a string to a bool" if s.lower() in ["1", "true", "yes", "on"]: return True else: return False def str_is_runlevel(s): "validate the given string as a system run-level" if len(s) != 1: return False if s in '12345': return True return False def str_is_timezone(s): "validate the given string as a timezone file" for i in s: if i not in string.ascii_letters + string.digits + " -_./": return False return True def str_is_tty_dev(s): "validate the given string as a tty device, eg. 'ttyS0'" if len(s.split()) != 1: return False if re.match('ttyS[0-9]$', s): return True if re.match('tty0$', s): return True if re.match('tty[1-9][0-9]?$', s): return True return False def str_is_baud(s): "validate the given string as a tty baud rate" if s in ('110', '300', '1200', '2400', '4800', '9600', '19200', '38400', \ '57600', '115200'): return True else: return False def str_is_hostname(s): "validate the given string as a hostname" if len(s.split()) != 1: return False for i in s: if i not in string.ascii_letters + string.digits + "-_.": return False return True def str_is_ipaddr(s): "validate the given string as an IP address" ss = s.split('.') if len(ss) != 4: return False for i in ss: for c in i: if c not in string.digits: return False try: n = int(i) except ValueError: return False if n < 0 or n > 255: return False return True def str_is_prefix(s): "validate the given string as an address prefix length (for netmask)" for c in s: if c not in string.digits: return False try: n = int(s) except ValueError: return False else: if n >= 1 and n <= 31: return True else: return False def str_is_netmask(s): "validate the given string as a netmask" ss = s.split('.') if len(ss) != 4: return False for i in ss: for c in i: if c not in string.digits: return False try: n = int(i) except ValueError: return False if n < 0 or n > 255: return False # XXX need to validate netmask has high-order bits only return True def str_is_domain(s): "validate the given string as a domain name" if len(s.split()) != 1: return False for i in s: if i not in string.ascii_letters + string.digits + "-_.": return False return True def str_is_eth_dev(s): "validate the given string as a eth device, eg. 'eth0'" if len(s.split()) != 1: return False if re.match('eth0$', s): return True if re.match('eth[1-9][0-9]?$', s): return True return False def str_is_file_path(s): "validate the given string as a valid full (absolute) file path" if not s: return False if s[:1] != '/': return False if s.find('//') != -1: return False for i in s: if i not in string.ascii_letters + string.digits + "- _./": return False return True def str_is_file_name(s): "validate the given string as a valid file base-name" if not s: return False for i in s: if i not in string.ascii_letters + string.digits + "- _.": return False return True def str_is_pxe_name(s): "validate the given string as a valid pxe image name" if len(s.split()) != 1: return False for i in s: if i not in string.ascii_letters + string.digits + "-_.": return False return True def str_is_macaddr(s): "validate the given string as an Ethernet MAC address" ss = s.split(':') if len(ss) != 6: return False for i in ss: if not re.match('^[0-9a-f][0-9a-f]$', i, re.IGNORECASE): return False return True def str_is_disk_name(s): "validate the given string as a disk name, eg. 'sda'" if len(s.split()) != 1: return False if re.match('sd[a-z]$', s): return True if re.match('vd[a-z]$', s): return True if re.match('nvme[0-9]n[1-9][0-9]?$', s): return True if re.match('mmcblk[0-9][0-9]?$', s): return True return False def str_is_fstype(s): "validate the given string as a filesystem type" if s not in FS_TYPES: return False return True def str_is_special_fstype(s): "validate the given string as a filesystem type" if s not in SPECIAL_FS_TYPES: return False return True def str_is_fsopts(s): """ Validate the given string as a list of filesystem mount options. The list must be comma-separated and have no spaces in it. """ for o in s.split(','): if o not in FS_OPTIONS and o not in EXTRA_FS_OPTIONS: return False return True def str_is_mountpoint(s): "validate the given string as a mountpoint path" if len(s.split()) != 1: return False if s[:1] != '/': return False if s.find('//') != -1: return False # don't allow trailing '/' as in "/home/" if len(s) > 1 and s[-1] == '/': return False for i in s: if i not in string.ascii_letters + string.digits + "-_./": return False return True def str_is_disk_dev(s): """ Validate the given string as a whole-disk device (eg, /dev/sda). """ if not s.startswith('/dev/'): return False s = s.replace('/dev/', '') return str_is_disk_name(s) def str_is_loop_disk_dev(s): """ Validate the given string as a whole-disk loopback mapper disk devices (eg, /dev/mapper/loop0). """ if not s.startswith('/dev/'): return False s = s.replace('/dev/', '') if not s.startswith('mapper/loop'): return False num = s.replace('mapper/loop', '') if not num: return False for c in num: if c not in string.digits: return False return True def str_is_disk_part_dev(s): """ Validate the given string as a disk partition device (eg, /dev/sda2). """ if not s.startswith('/dev/'): return False s = s.replace('/dev/', '') if len(s) < 4: return False if s[:2] not in ['sd', 'vd', 'nv', 'mm']: return False if s[:2] == 'nv': # Handle NVMe devices # Assume device names like 'nvme0n1p1' - 'nvme9n99p99' if s[:4] != 'nvme': return False if s[4] not in string.digits: return False if s[5] != 'n': return False x = s[6:].split('p') if len(x) != 2: return False unit = x[0] pnum = x[1] if len(unit) < 1 or len(unit) > 2: return False for c in unit: if c not in string.digits: return False for c in pnum: if c not in string.digits: return False elif s[:2] == 'mm': # Handle eMMC devices # Assume device names like 'mmcblk0p1' - 'mmcblk99p99' if s[:6] != 'mmcblk': return False x = s[6:].split('p') if len(x) != 2: return False unit = x[0] pnum = x[1] if len(unit) < 1 or len(unit) > 2: return False for c in unit: if c not in string.digits: return False for c in pnum: if c not in string.digits: return False else: # Assume device names like 'sda1' - 'sdz99' if s[2] not in string.ascii_lowercase: return False for c in s[3:]: if c not in string.digits: return False return True def str_is_lvm_dev(s): """ Validate the given string as an LVM LV device "/dev/{vg_name}/{lv_name}" (eg, "/dev/centos/home"). """ if not s.startswith('/dev/'): return False s = s.replace('/dev/', '') x = s.split('/') if len(x) != 2: return False (vg, lv) = x if not str_is_lvm_vg_name(vg): return False if not str_is_lvm_lv_name(lv): return False return True def str_is_lvm_vg_name(s): """ Validate the given string as a valid LVM VG name, see lvm(1). """ if not s: return False for i in s: if i not in string.ascii_letters + string.digits + "+_.-": return False if s[0] == '-': return False # don't clash with mapper and raid devices if s in ['mapper', 'md']: return False return True def str_is_lvm_lv_name(s): """ Validate the given string as a valid LVM LV name, see lvm(1). """ if not s: return False for i in s: if i not in string.ascii_letters + string.digits + "+_.-": return False if s[0] == '-': return False if s in ['snapshot', 'pvmove']: return False for i in ['_cdata', '_cmeta', '_corig', '_mlog', '_mimage', '_pmspare', '_rimage', '_rmeta', '_tdata', '_tmeta', '_vorigin', '_vdata']: if i in s: return False return True def str_is_raid_name(s): """ Validate the given string as a valid RAID name. """ if not s: return False for i in s: if i not in string.ascii_letters + string.digits + "+_.-": return False if s[0] == '-': return False return True def str_is_raid_dev(s): """ Validate the given string as a RAID device "/dev/md/{raid_name}" (eg, "/dev/md/home"). """ if not s.startswith('/dev/'): return False s = s.replace('/dev/', '') x = s.split('/') if len(x) != 2: return False if x[0] != 'md': return False if not str_is_raid_name(x[1]): return False return True