"""Compute the number of PVs needed for an LV (with exclusive storage).
@type size: float
- @param size: LV size
+ @param size: LV size in MiB
@param pvs_info: list of objects.LvmPvInfo, cannot be empty
@rtype: integer
@return: number of PVs needed
stripes = min(current_pvs, desired_stripes)
if excl_stor:
- err_msgs = utils.LvmExclusiveCheckNodePvs(pvs_info)
+ (err_msgs, _) = utils.LvmExclusiveCheckNodePvs(pvs_info)
if err_msgs:
for m in err_msgs:
logging.warning(m)
return data
@classmethod
- def GetPVInfo(cls, vg_names, filter_allocatable=True):
+ def GetPVInfo(cls, vg_names, filter_allocatable=True, include_lvs=False):
"""Get the free space info for PVs in a volume group.
@param vg_names: list of volume group names, if empty all will be returned
@param filter_allocatable: whether to skip over unallocatable PVs
+ @param include_lvs: whether to include a list of LVs hosted on each PV
@rtype: list
@return: list of objects.LvmPvInfo objects
"""
+ # We request "lv_name" field only if we care about LVs, so we don't get
+ # a long list of entries with many duplicates unless we really have to.
+ # The duplicate "pv_name" field will be ignored.
+ if include_lvs:
+ lvfield = "lv_name"
+ else:
+ lvfield = "pv_name"
try:
info = cls._GetVolumeInfo("pvs", ["pv_name", "vg_name", "pv_free",
- "pv_attr", "pv_size"])
+ "pv_attr", "pv_size", lvfield])
except errors.GenericError, err:
logging.error("Can't get PV information: %s", err)
return None
+ # When asked for LVs, "pvs" may return multiple entries for the same PV-LV
+ # pair. We sort entries by PV name and then LV name, so it's easy to weed
+ # out duplicates.
+ if include_lvs:
+ info.sort(key=(lambda i: (i[0], i[5])))
data = []
- for (pv_name, vg_name, pv_free, pv_attr, pv_size) in info:
+ lastpvi = None
+ for (pv_name, vg_name, pv_free, pv_attr, pv_size, lv_name) in info:
# (possibly) skip over pvs which are not allocatable
if filter_allocatable and pv_attr[0] != "a":
continue
# (possibly) skip over pvs which are not in the right volume group(s)
if vg_names and vg_name not in vg_names:
continue
- pvi = objects.LvmPvInfo(name=pv_name, vg_name=vg_name,
- size=float(pv_size), free=float(pv_free),
- attributes=pv_attr)
- data.append(pvi)
+ # Beware of duplicates (check before inserting)
+ if lastpvi and lastpvi.name == pv_name:
+ if include_lvs and lv_name:
+ if not lastpvi.lv_list or lastpvi.lv_list[-1] != lv_name:
+ lastpvi.lv_list.append(lv_name)
+ else:
+ if include_lvs and lv_name:
+ lvl = [lv_name]
+ else:
+ lvl = []
+ lastpvi = objects.LvmPvInfo(name=pv_name, vg_name=vg_name,
+ size=float(pv_size), free=float(pv_free),
+ attributes=pv_attr, lv_list=lvl)
+ data.append(lastpvi)
return data
@classmethod
- def GetVGInfo(cls, vg_names, filter_readonly=True):
+ def _GetExclusiveStorageVgFree(cls, vg_name):
+ """Return the free disk space in the given VG, in exclusive storage mode.
+
+ @type vg_name: string
+ @param vg_name: VG name
+ @rtype: float
+ @return: free space in MiB
+ """
+ pvs_info = cls.GetPVInfo([vg_name])
+ if not pvs_info:
+ return 0.0
+ pv_size = cls._GetStdPvSize(pvs_info)
+ num_pvs = len(cls._GetEmptyPvNames(pvs_info))
+ return pv_size * num_pvs
+
+ @classmethod
+ def GetVGInfo(cls, vg_names, excl_stor, filter_readonly=True):
"""Get the free space info for specific VGs.
@param vg_names: list of volume group names, if empty all will be returned
+ @param excl_stor: whether exclusive_storage is enabled
@param filter_readonly: whether to skip over readonly VGs
@rtype: list
# (possibly) skip over vgs which are not in the right volume group(s)
if vg_names and vg_name not in vg_names:
continue
+ # Exclusive storage needs a different concept of free space
+ if excl_stor:
+ es_free = cls._GetExclusiveStorageVgFree(vg_name)
+ assert es_free <= vg_free
+ vg_free = es_free
data.append((float(vg_free), float(vg_size), vg_name))
return data
snap = LogicalVolume((self._vg_name, snap_name), None, size, self.params)
_IgnoreError(snap.Remove)
- vg_info = self.GetVGInfo([self._vg_name])
+ vg_info = self.GetVGInfo([self._vg_name], False)
if not vg_info:
_ThrowError("Can't compute VG info for vg %s", self._vg_name)
free_size, _, _ = vg_info[0]