def testBoundarySizeCases(self): medpv1 = self._MED_PV.Copy() medpv2 = self._MED_PV.Copy() (errmsgs, (small, big)) = utils.LvmExclusiveCheckNodePvs( [medpv1, medpv2, self._MED_PV]) self.assertFalse(errmsgs) self.assertEqual(small, self._MED_PV.size) self.assertEqual(big, self._MED_PV.size) # Just within the margins medpv1.size = self._MED_PV.size * (1 - constants.PART_MARGIN + self._EPS) medpv2.size = self._MED_PV.size * (1 + constants.PART_MARGIN - self._EPS) (errmsgs, (small, big)) = utils.LvmExclusiveCheckNodePvs( [medpv1, medpv2, self._MED_PV]) self.assertFalse(errmsgs) self.assertEqual(small, medpv1.size) self.assertEqual(big, medpv2.size) # Just outside the margins medpv1.size = self._MED_PV.size * (1 - constants.PART_MARGIN - self._EPS) medpv2.size = self._MED_PV.size * (1 + constants.PART_MARGIN) (errmsgs, (small, big)) = utils.LvmExclusiveCheckNodePvs( [medpv1, medpv2, self._MED_PV]) self.assertTrue(errmsgs) self.assertEqual(small, medpv1.size) self.assertEqual(big, medpv2.size) medpv1.size = self._MED_PV.size * (1 - constants.PART_MARGIN) medpv2.size = self._MED_PV.size * (1 + constants.PART_MARGIN + self._EPS) (errmsgs, (small, big)) = utils.LvmExclusiveCheckNodePvs( [medpv1, medpv2, self._MED_PV]) self.assertTrue(errmsgs) self.assertEqual(small, medpv1.size) self.assertEqual(big, medpv2.size)
def testEqualPvs(self): (errmsgs, (small, big)) = utils.LvmExclusiveCheckNodePvs([self._MED_PV] * 2) self.assertFalse(errmsgs) self.assertEqual(small, self._MED_PV.size) self.assertEqual(big, self._MED_PV.size) (errmsgs, (small, big)) = utils.LvmExclusiveCheckNodePvs([self._SMALL_PV] * 3) self.assertFalse(errmsgs) self.assertEqual(small, self._SMALL_PV.size) self.assertEqual(big, self._SMALL_PV.size)
def testTooDifferentPvs(self): (errmsgs, (small, big)) = utils.LvmExclusiveCheckNodePvs([self._MED_PV, self._BIG_PV]) self.assertEqual(len(errmsgs), 1) self.assertEqual(small, self._MED_PV.size) self.assertEqual(big, self._BIG_PV.size) (errmsgs, (small, big)) = utils.LvmExclusiveCheckNodePvs( [self._MED_PV, self._SMALL_PV]) self.assertEqual(len(errmsgs), 1) self.assertEqual(small, self._SMALL_PV.size) self.assertEqual(big, self._MED_PV.size)
def CheckNodePVs(nresult, exclusive_storage): """Check node PVs. """ pvlist_dict = nresult.get(constants.NV_PVLIST, None) if pvlist_dict is None: return (["Can't get PV list from node"], None) pvlist = map(objects.LvmPvInfo.FromDict, pvlist_dict) errlist = [] # check that ':' is not present in PV names, since it's a # special character for lvcreate (denotes the range of PEs to # use on the PV) for pv in pvlist: if ":" in pv.name: errlist.append("Invalid character ':' in PV '%s' of VG '%s'" % (pv.name, pv.vg_name)) es_pvinfo = None if exclusive_storage: (errmsgs, es_pvinfo) = utils.LvmExclusiveCheckNodePvs(pvlist) errlist.extend(errmsgs) shared_pvs = nresult.get(constants.NV_EXCLUSIVEPVS, None) if shared_pvs: for (pvname, lvlist) in shared_pvs: # TODO: Check that LVs are really unrelated (snapshots, DRBD meta...) errlist.append("PV %s is shared among unrelated LVs (%s)" % (pvname, utils.CommaJoin(lvlist))) return (errlist, es_pvinfo)
def testOnePv(self): (errmsgs, (small, big)) = utils.LvmExclusiveCheckNodePvs([self._MED_PV]) self.assertFalse(errmsgs) self.assertEqual(small, self._MED_PV.size) self.assertEqual(big, self._MED_PV.size)
def Create(cls, unique_id, children, size, spindles, params, excl_stor, dyn_params, **kwargs): """Create a new logical volume. """ if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2: 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: if excl_stor: msg = "No (empty) PVs found" else: msg = "Can't compute PV info for vg %s" % vg_name base.ThrowError(msg) pvs_info.sort(key=(lambda pv: pv.free), reverse=True) pvlist = [pv.name for pv in pvs_info] if compat.any(":" in v for v in pvlist): base.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'") current_pvs = len(pvlist) desired_stripes = params[constants.LDP_STRIPES] stripes = min(current_pvs, desired_stripes) if excl_stor: if spindles is None: base.ThrowError("Unspecified number of spindles: this is required" "when exclusive storage is enabled, try running" " gnt-cluster repair-disk-sizes") (err_msgs, _) = utils.LvmExclusiveCheckNodePvs(pvs_info) if err_msgs: for m in err_msgs: logging.warning(m) req_pvs = cls._ComputeNumPvs(size, pvs_info) if spindles < req_pvs: base.ThrowError("Requested number of spindles (%s) is not enough for" " a disk of %d MB (at least %d spindles needed)", spindles, size, req_pvs) else: req_pvs = spindles pvlist = cls._GetEmptyPvNames(pvs_info, req_pvs) current_pvs = len(pvlist) if current_pvs < req_pvs: base.ThrowError("Not enough empty PVs (spindles) to create a disk of %d" " MB: %d available, %d needed", size, current_pvs, req_pvs) assert current_pvs == len(pvlist) # We must update stripes to be sure to use all the desired spindles stripes = current_pvs if stripes > desired_stripes: # Don't warn when lowering stripes, as it's no surprise logging.warning("Using %s stripes instead of %s, to be able to use" " %s spindles", stripes, desired_stripes, current_pvs) else: if stripes < desired_stripes: logging.warning("Could not use %d stripes for VG %s, as only %d PVs are" " available.", desired_stripes, vg_name, current_pvs) free_size = sum([pv.free for pv in pvs_info]) # The size constraint should have been checked from the master before # calling the create function. if free_size < size: base.ThrowError("Not enough free space: required %s," " available %s", size, free_size) # If the free space is not well distributed, we won't be able to # create an optimally-striped volume; in that case, we want to try # with N, N-1, ..., 2, and finally 1 (non-stripped) number of # stripes cmd = ["lvcreate", "-L%dm" % size, "-n%s" % lv_name] for stripes_arg in range(stripes, 0, -1): result = utils.RunCmd(cmd + ["-i%d" % stripes_arg] + [vg_name] + pvlist) if not result.failed: break if result.failed: base.ThrowError("LV create failed (%s): %s", result.fail_reason, result.output) return LogicalVolume(unique_id, children, size, params, dyn_params, **kwargs)