# # Copyright (c) 2018 Red Hat, Inc. # # This program 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 2 # of the License, or (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301, USA. # """ Defaults - manage Albireo/VDO defaults $Id: //eng/vdo-releases/magnesium/src/python/vdo/vdomgmnt/Defaults.py#17 $ """ from . import Constants, MgmntLogger, MgmntUtils, SizeString, UserExitStatus import os import re import stat class ArgumentError(UserExitStatus, Exception): """Exception raised to indicate an error with an argument.""" ###################################################################### # Overridden methods ###################################################################### def __init__(self, msg, *args, **kwargs): super(ArgumentError, self).__init__(*args, **kwargs) self._msg = msg ###################################################################### def __str__(self): return self._msg ######################################################################## class Defaults(object): """Defaults manages default values for arguments.""" NOTSET = -1 ackThreads = 1 ackThreadsMax = 100 ackThreadsMin = 0 activate = Constants.enabled bioRotationInterval = 64 bioRotationIntervalMax = 1024 bioRotationIntervalMin = 1 bioThreadOverheadMB = 18 # MB bioThreadReadCacheOverheadMB = 1.12 # per read cache MB overhead in MB bioThreads = 4 bioThreadsMax = 100 bioThreadsMin = 1 blockMapCacheSize = SizeString("128M") blockMapCacheSizeMaxPlusOne = SizeString("16T") blockMapCacheSizeMin = SizeString("128M") blockMapCacheSizeMinPerLogicalThread = 16 * MgmntUtils.MiB blockMapPeriod = 16380 blockMapPeriodMax = 16380 blockMapPeriodMin = 1 cfreq = 0 confFile = os.getenv('VDO_CONF_DIR', '/etc') + '/vdoconf.yml' compression = Constants.enabled deduplication = Constants.enabled cpuThreads = 2 cpuThreadsMax = 100 cpuThreadsMin = 1 emulate512 = Constants.disabled hashZoneThreads = 1 hashZoneThreadsMax = 100 hashZoneThreadsMin = 0 indexMem = 0.25 indexMemIntMax = 1024 indexMemIntMin = 1 log = MgmntLogger.getLogger(MgmntLogger.myname + '.Defaults') logicalSizeMax = SizeString("4096T") logicalThreads = 1 logicalThreadsBlockMapCacheSizeThreshold = 9 logicalThreadsMax = 100 logicalThreadsMin = 0 mdRaid5Mode = 'on' physicalThreadOverheadMB = 10 # MB physicalThreads = 1 physicalThreadsMax = 16 physicalThreadsMin = 0 readCache = Constants.disabled readCacheSize = SizeString("0") readCacheSizeMaxPlusOne = SizeString("16T") readCacheSizeMin = SizeString("0") slabSize = SizeString("2G") slabSizeMax = SizeString("32G") slabSizeMin = SizeString("128M") sparseIndex = Constants.disabled udsParallelFactor = 0 vdoPhysicalBlockSize = 4096 vdoLogLevel = 'info' vdoLogLevelChoices = ['critical', 'error', 'warning', 'notice', 'info', 'debug'] vdoTargetName = 'vdo' writePolicy = 'auto' writePolicyChoices = ['async', 'sync', 'auto'] ###################################################################### # Public methods ###################################################################### @staticmethod def checkAbspath(value): """Checks that an option is an absolute pathname. Arguments: value (str): Value provided as an argument to the option. Returns: The pathname as a string. Raises: ArgumentError """ if os.path.isabs(value): return value raise ArgumentError(_("must be an absolute pathname")) ###################################################################### @staticmethod def checkBlkDev(value): """Checks that an option is a valid name for the backing store. Arguments: value (str): Value provided as an argument to the option. Returns: The backing store name as a string. Raises: ArgumentError """ return Defaults.checkAbspath(value) ###################################################################### @staticmethod def checkBlockMapPeriod(value): """Checks that an option is an acceptable value for the block map period. The number must be at least 1 and no bigger than 16380. Arguments: value (str): Value provided as an argument to the option. Returns: The value converted to an int. Raises: ArgumentError """ return Defaults._rangeCheck(Defaults.blockMapPeriodMin, Defaults.blockMapPeriodMax, value) ###################################################################### @staticmethod def checkConfFile(value): """Checks that an option specifies a possible config file path name. Currently the only restriction is that the if the path already exists, it must be a regular file. Arguments: value (str): Value provided as an argument to the option. Returns: The value. Raises: ArgumentError """ if value is not None and not os.path.isfile(value): # Get a nicer error message if value refers to a directory or # block device, anything else will be handled later. return Defaults._checkNotBlockFileOrDirectory(value) return value ###################################################################### @staticmethod def checkIndexmem(value): """Checks that an option is a legitimate index memory setting. To handle non-US locales while still supporting the documented behavior, we allow either a period or a comma as the decimal separator. This will be normalized to the decimal-point representation; internally indexMem is always either the string representation of an integer or one of the exact strings '0.25', '0.5', or '0.75' regardless of locale. Arguments: value (str): Value provided as an argument to the option. Returns: The memory setting as a string. Raises: ArgumentError """ value = value.replace(",", ".") try: if value == '0.25' or value == '0.5' or value == '0.75': return value number = int(value) if Defaults.indexMemIntMin <= number <= Defaults.indexMemIntMax: return value except ValueError: pass raise ArgumentError( _("must be an integer at least {0} and less than or equal to {1}" " or one of the special values of 0.25, 0.5, or 0.75") .format(Defaults.indexMemIntMin, Defaults.indexMemIntMax)) ###################################################################### @staticmethod def checkLogFile(value): """Checks that an option specifies a possible log file path name. Currently the only restriction is that the path name may not refer to an existing block device node or directory in the file system. Arguments: value (str): Value provided as an argument to the option. Returns: The value. Raises: ArgumentError """ return Defaults._checkNotBlockFileOrDirectory(value) ###################################################################### @staticmethod def checkLogicalSize(value): """Checks that an option is an LVM-style size string. Arguments: value (str): Value provided as an argument to the option. Returns: The value converted to a SizeString. Raises: ArgumentError """ ss = Defaults.checkSize(value) if not (ss <= Defaults.logicalSizeMax): raise ArgumentError( _("must be less than or equal to {0}").format(Defaults.logicalSizeMax)) return ss ###################################################################### @staticmethod def checkPageCachesz(value): """Checks that an option is an acceptable value for the page cache size. Page cache sizes will be rounded up to a multiple of the page size, and must be at least 128M and less than 16T. Arguments: value (str): Value provided as an argument to the option. Returns: The value converted to a SizeString. Raises: ArgumentError """ ss = Defaults.checkSiSize(value) if (not (Defaults.blockMapCacheSizeMin <= ss < Defaults.blockMapCacheSizeMaxPlusOne)): raise ArgumentError( _("must be at least {0} and less than {1}") .format(Defaults.blockMapCacheSizeMin, Defaults.blockMapCacheSizeMaxPlusOne)) return ss ###################################################################### @staticmethod def checkReadCachesz(value): """Checks that an option is an acceptable value for the read cache size. Read cache sizes will be rounded to a multiple of 4k, and must be less than 16T. Arguments: value (str): Value provided as an argument to the option. Returns: The value converted to a SizeString. Raises: ArgumentError """ ss = Defaults.checkSiSize(value) if (not (Defaults.readCacheSizeMin <= ss < Defaults.readCacheSizeMaxPlusOne)): raise ArgumentError( _("must be at least {0} and less than {1}") .format(Defaults.readCacheSizeMin, Defaults.readCacheSizeMaxPlusOne)) return ss ###################################################################### @staticmethod def checkPhysicalThreadCount(value): """Checks that an option is a valid "physical" thread count. Arguments: value (str): Value provided as an argument to the option. Returns: The value converted to an integer. Raises: ArgumentError """ return Defaults._rangeCheck(Defaults.physicalThreadsMin, Defaults.physicalThreadsMax, value) ###################################################################### @staticmethod def checkRotationInterval(value): """Checks that an option is a valid bio rotation interval. Arguments: value (str): Value provided as an argument to the option. Returns: The value converted to an integer. Raises: ArgumentError """ return Defaults._rangeCheck(Defaults.bioRotationIntervalMin, Defaults.bioRotationIntervalMax, value) ###################################################################### @staticmethod def checkSiSize(value): """Checks that an option is an SI unit size string. Arguments: value (str): Value provided as an argument to the option. Returns: The value converted to a SizeString. Raises: ArgumentError """ if (value[-1].isdigit() or (value[-1].isalpha() and (value[-1].lower() in Constants.lvmSiSuffixes))): try: ss = SizeString(value) return ss except ValueError: pass raise ArgumentError(_("must be an SI-style size string")) ###################################################################### @staticmethod def checkSize(value): """Checks that an option is an LVM-style size string. Arguments: value (str): Value provided as an argument to the option. Returns: The value converted to a SizeString. Raises: ArgumentError """ try: ss = SizeString(value) return ss except ValueError: pass raise ArgumentError(_("must be an LVM-style size string")) ###################################################################### @staticmethod def checkSlabSize(value): """Checks that an option is a valid slab size. Arguments: value (str): Value provided as an argument to the option. Returns: The value converted to a SizeString. Raises: ArgumentError """ try: ss = SizeString(value) size = ss.toBytes() if ((not MgmntUtils.isPowerOfTwo(size)) or (not (Defaults.slabSizeMin <= ss <= Defaults.slabSizeMax))): raise ArgumentError( _("must be a power of two between {0} and {1}") .format(Defaults.slabSizeMin, Defaults.slabSizeMax)) return ss except ValueError: pass raise ArgumentError(_("must be an LVM-style size string")) ###################################################################### @staticmethod def checkThreadCount0_100(value): """Checks that an option is a valid thread count, for worker thread types requiring between 0 and 100 threads, inclusive. Arguments: value (str): Value provided as an argument to the option. Returns: The value converted to an integer. Raises: ArgumentError """ return Defaults._rangeCheck(0, 100, value) ###################################################################### @staticmethod def checkThreadCount1_100(value): """Checks that an option is a valid thread count, for worker thread types requiring between 1 and 100 threads, inclusive. Arguments: value (str): Value provided as an argument to the option. Returns: The value converted to an integer. Raises: ArgumentError """ return Defaults._rangeCheck(1, 100, value) ###################################################################### @staticmethod def checkVDOName(value): """Checks that an option is a valid VDO device name. The "dmsetup create" command will accept a lot of characters that could be problematic for udev or for running shell commands without careful quoting. For now, we permit only alphanumerics and a few other characters. Arguments: value (str): Value provided as an argument to the option. Returns: The device name as a string. Raises: ArgumentError """ # See "whitelisted" characters in # https://sourceware.org/git/?p=lvm2.git;a=blob;f=libdm/libdm-common.c;h=e983b039276671cae991f9b8b81328760aacac4a;hb=HEAD # # N.B.: (1) Moved "-" last so we can put in it brackets in a # regexp. (2) Removed "=" so "-name=foo" ("-n ame=foo") gets an # error. allowedChars = "A-Za-z0-9#+.:@_-" if re.match("^[" + allowedChars + "]*$", value) is None: raise ArgumentError( _("VDO device names may only contain characters" " in '{0}': bad value '{1}'").format(allowedChars, value)) if value.startswith("-"): raise ArgumentError( _("VDO device names may not start with '-': bad value '{0}'") .format(value)) return value ###################################################################### # Overridden methods ###################################################################### def __init__(self): pass ###################################################################### @staticmethod def _checkNotBlockFileOrDirectory(value): """Checks that an option does not specify an existing block device or directory. Arguments: value (str): Value provided as an argument to the option. Returns: The value. Raises: ArgumentError """ if value is not None and os.path.exists(value): pathstat = os.stat(value) if stat.S_ISBLK(pathstat.st_mode): raise ArgumentError(_("{0} is a block device").format(value)) if stat.S_ISDIR(pathstat.st_mode): raise ArgumentError(_("{0} is a directory").format(value)) return value ###################################################################### @staticmethod def _rangeCheck(minValue, maxValue, value): """Checks that an option is a valid integer within a desired range. Arguments: minValue (int): The minimum acceptable value. maxValue (int): The maximum acceptable value. value (str): Value provided as an argument to the option. Returns: The value converted to an integer. Raises: ArgumentError """ try: number = int(value) if minValue <= number <= maxValue: return number except ValueError: pass raise ArgumentError( _("must be an integer at least {0} and less than or equal to {1}") .format(minValue, maxValue))