X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/89b70f39e6f200538c08dfafe734ee9845a46ccb..d63997b36350f044add706d9daa8bd6db0bccc46:/lib/bdev.py diff --git a/lib/bdev.py b/lib/bdev.py index d9718a7..721f3a6 100644 --- a/lib/bdev.py +++ b/lib/bdev.py @@ -349,6 +349,10 @@ class LogicalVolume(BlockDev): """Logical Volume block device. """ + _VALID_NAME_RE = re.compile("^[a-zA-Z0-9+_.-]*$") + _INVALID_NAMES = frozenset([".", "..", "snapshot", "pvmove"]) + _INVALID_SUBSTRINGS = frozenset(["_mlog", "_mimage"]) + def __init__(self, unique_id, children, size): """Attaches to a LV device. @@ -359,7 +363,9 @@ class LogicalVolume(BlockDev): if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2: raise ValueError("Invalid configuration data %s" % str(unique_id)) self._vg_name, self._lv_name = unique_id - self.dev_path = "/dev/%s/%s" % (self._vg_name, self._lv_name) + self._ValidateName(self._vg_name) + self._ValidateName(self._lv_name) + self.dev_path = utils.PathJoin("/dev", self._vg_name, self._lv_name) self._degraded = True self.major = self.minor = self.pe_size = self.stripe_count = None self.Attach() @@ -373,6 +379,8 @@ class LogicalVolume(BlockDev): raise errors.ProgrammerError("Invalid configuration data %s" % str(unique_id)) vg_name, lv_name = unique_id + cls._ValidateName(vg_name) + cls._ValidateName(lv_name) pvs_info = cls.GetPVInfo([vg_name]) if not pvs_info: _ThrowError("Can't compute PV info for vg %s", vg_name) @@ -381,8 +389,9 @@ class LogicalVolume(BlockDev): pvlist = [ pv[1] for pv in pvs_info ] if utils.any(pvlist, lambda v: ":" in v): - _ThrowError("Some of your PVs have invalid character ':'" - " in their name") + _ThrowError("Some of your PVs have the invalid character ':' in their" + " name, this is not supported - please filter them out" + " in lvm.conf using either 'filter' or 'preferred_names'") free_size = sum([ pv[0] for pv in pvs_info ]) current_pvs = len(pvlist) stripes = min(current_pvs, constants.LVM_STRIPECOUNT) @@ -442,6 +451,20 @@ class LogicalVolume(BlockDev): return data + @classmethod + def _ValidateName(cls, name): + """Validates that a given name is valid as VG or LV name. + + The list of valid characters and restricted names is taken out of + the lvm(8) manpage, with the simplification that we enforce both + VG and LV restrictions on the names. + + """ + if (not cls._VALID_NAME_RE.match(name) or + name in cls._INVALID_NAMES or + utils.any(cls._INVALID_SUBSTRINGS, lambda x: x in name)): + _ThrowError("Invalid LVM name '%s'", name) + def Remove(self): """Remove this logical volume. @@ -469,7 +492,7 @@ class LogicalVolume(BlockDev): if result.failed: _ThrowError("Failed to rename the logical volume: %s", result.output) self._lv_name = new_name - self.dev_path = "/dev/%s/%s" % (self._vg_name, self._lv_name) + self.dev_path = utils.PathJoin("/dev", self._vg_name, self._lv_name) def Attach(self): """Attach to an existing LV. @@ -512,7 +535,7 @@ class LogicalVolume(BlockDev): try: major = int(major) minor = int(minor) - except ValueError, err: + except (TypeError, ValueError), err: logging.error("lvs major/minor cannot be parsed: %s", str(err)) try: @@ -792,6 +815,8 @@ class BaseDRBD(BlockDev): # pylint: disable-msg=W0223 """ _VERSION_RE = re.compile(r"^version: (\d+)\.(\d+)\.(\d+)" r" \(api:(\d+)/proto:(\d+)(?:-(\d+))?\)") + _VALID_LINE_RE = re.compile("^ *([0-9]+): cs:([^ ]+).*$") + _UNUSED_LINE_RE = re.compile("^ *([0-9]+): cs:Unconfigured$") _DRBD_MAJOR = 147 _ST_UNCONFIGURED = "Unconfigured" @@ -817,21 +842,20 @@ class BaseDRBD(BlockDev): # pylint: disable-msg=W0223 _ThrowError("Can't read any data from %s", filename) return data - @staticmethod - def _MassageProcData(data): + @classmethod + def _MassageProcData(cls, data): """Transform the output of _GetProdData into a nicer form. @return: a dictionary of minor: joined lines from /proc/drbd for that minor """ - lmatch = re.compile("^ *([0-9]+):.*$") results = {} old_minor = old_line = None for line in data: if not line: # completely empty lines, as can be returned by drbd8.0+ continue - lresult = lmatch.match(line) + lresult = cls._VALID_LINE_RE.match(line) if lresult is not None: if old_minor is not None: results[old_minor] = old_line @@ -892,9 +916,8 @@ class BaseDRBD(BlockDev): # pylint: disable-msg=W0223 data = cls._GetProcData() used_devs = {} - valid_line = re.compile("^ *([0-9]+): cs:([^ ]+).*$") for line in data: - match = valid_line.match(line) + match = cls._VALID_LINE_RE.match(line) if not match: continue minor = int(match.group(1)) @@ -933,7 +956,7 @@ class BaseDRBD(BlockDev): # pylint: disable-msg=W0223 result.fail_reason, result.output) try: sectors = int(result.stdout) - except ValueError: + except (TypeError, ValueError): _ThrowError("Invalid output from blockdev: '%s'", result.stdout) bytes = sectors * 512 if bytes < 128 * 1024 * 1024: # less than 128MiB @@ -1025,14 +1048,12 @@ class DRBD8(BaseDRBD): """ data = cls._GetProcData() - unused_line = re.compile("^ *([0-9]+): cs:Unconfigured$") - used_line = re.compile("^ *([0-9]+): cs:") highest = None for line in data: - match = unused_line.match(line) + match = cls._UNUSED_LINE_RE.match(line) if match: return int(match.group(1)) - match = used_line.match(line) + match = cls._VALID_LINE_RE.match(line) if match: minor = int(match.group(1)) highest = max(highest, minor) @@ -1909,9 +1930,11 @@ class FileStorage(BlockDev): DEV_MAP = { constants.LD_LV: LogicalVolume, constants.LD_DRBD8: DRBD8, - constants.LD_FILE: FileStorage, } +if constants.ENABLE_FILE_STORAGE: + DEV_MAP[constants.LD_FILE] = FileStorage + def FindDevice(dev_type, unique_id, children, size): """Search for an existing, assembled device.