diff --git a/Makefile.am b/Makefile.am index 563d31eff62b4fe2effad0edfb2167093a4dbeaa..39271b5fff1b6cd0fe3a4ca9eeaac44402a00fad 100644 --- a/Makefile.am +++ b/Makefile.am @@ -345,6 +345,7 @@ utils_PYTHON = \ lib/utils/hash.py \ lib/utils/io.py \ lib/utils/log.py \ + lib/utils/lvm.py \ lib/utils/mlock.py \ lib/utils/nodesetup.py \ lib/utils/process.py \ diff --git a/lib/backend.py b/lib/backend.py index c3279a73d393f0861810a3ae10d59f6354162fab..4b97e9bba69c30cee27411382e824c6b3a6e7490 100644 --- a/lib/backend.py +++ b/lib/backend.py @@ -762,9 +762,9 @@ def VerifyNode(what, cluster_name): result[constants.NV_VGLIST] = utils.ListVolumeGroups() if constants.NV_PVLIST in what and vm_capable: - result[constants.NV_PVLIST] = \ - bdev.LogicalVolume.GetPVInfo(what[constants.NV_PVLIST], - filter_allocatable=False) + val = bdev.LogicalVolume.GetPVInfo(what[constants.NV_PVLIST], + filter_allocatable=False) + result[constants.NV_PVLIST] = map(objects.LvmPvInfo.ToDict, val) if constants.NV_VERSION in what: result[constants.NV_VERSION] = (constants.PROTOCOL_VERSION, diff --git a/lib/bdev.py b/lib/bdev.py index c452e577faa4c382e2c37f1914ba3513dd1dbe33..8201c267d6501267b36f6d96f0df335e0fa31fcf 100644 --- a/lib/bdev.py +++ b/lib/bdev.py @@ -537,15 +537,14 @@ class LogicalVolume(BlockDev): pvs_info = cls.GetPVInfo([vg_name]) if not pvs_info: _ThrowError("Can't compute PV info for vg %s", vg_name) - pvs_info.sort() - pvs_info.reverse() + pvs_info.sort(key=(lambda pv: pv.free), reverse=True) - pvlist = [pv[1] for pv in pvs_info] + pvlist = [pv.name for pv in pvs_info] if compat.any(":" in v for v in pvlist): _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]) + free_size = sum([pv.free for pv in pvs_info]) current_pvs = len(pvlist) desired_stripes = params[constants.LDP_STRIPES] stripes = min(current_pvs, desired_stripes) @@ -613,25 +612,28 @@ class LogicalVolume(BlockDev): @param filter_allocatable: whether to skip over unallocatable PVs @rtype: list - @return: list of tuples (free_space, name) with free_space in mebibytes + @return: list of objects.LvmPvInfo objects """ try: info = cls._GetVolumeInfo("pvs", ["pv_name", "vg_name", "pv_free", - "pv_attr"]) + "pv_attr", "pv_size"]) except errors.GenericError, err: logging.error("Can't get PV information: %s", err) return None data = [] - for pv_name, vg_name, pv_free, pv_attr in info: + for (pv_name, vg_name, pv_free, pv_attr, pv_size) 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 - data.append((float(pv_free), pv_name, vg_name)) + pvi = objects.LvmPvInfo(name=pv_name, vg_name=vg_name, + size=float(pv_size), free=float(pv_free), + attributes=pv_attr) + data.append(pvi) return data diff --git a/lib/cmdlib.py b/lib/cmdlib.py index 3b866646e9d550a72ebc8266e9e205d392eba0a1..d023686908bfa801a809997335b5ecdce591cb9e 100644 --- a/lib/cmdlib.py +++ b/lib/cmdlib.py @@ -2424,19 +2424,20 @@ class LUClusterVerifyGroup(LogicalUnit, _VerifyErrors): constants.MIN_VG_SIZE) _ErrorIf(vgstatus, constants.CV_ENODELVM, node, vgstatus) - # check pv names - pvlist = nresult.get(constants.NV_PVLIST, None) - test = pvlist is None + # check pv names (and possibly sizes) + pvlist_dict = nresult.get(constants.NV_PVLIST, None) + test = pvlist_dict is None _ErrorIf(test, constants.CV_ENODELVM, node, "Can't get PV list from node") if not test: + pvlist = map(objects.LvmPvInfo.FromDict, pvlist_dict) # 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 _, pvname, owner_vg in pvlist: - test = ":" in pvname + for pv in pvlist: + test = ":" in pv.name _ErrorIf(test, constants.CV_ENODELVM, node, "Invalid character ':' in PV '%s' of VG '%s'", - pvname, owner_vg) + pv.name, pv.vg_name) def _VerifyNodeBridges(self, ninfo, nresult, bridges): """Check the node bridges. diff --git a/lib/objects.py b/lib/objects.py index e08bd06f9cd8be9523f0d4e4b8b744afc8bf0eaa..2a0b8bda088dd1493910623016cf5bd700a7afbf 100644 --- a/lib/objects.py +++ b/lib/objects.py @@ -2059,3 +2059,38 @@ class SerializableConfigParser(ConfigParser.SafeConfigParser): cfp = cls() cfp.readfp(buf) return cfp + + +class LvmPvInfo(ConfigObject): + """Information about an LVM physical volume (PV). + + @type name: string + @ivar name: name of the PV + @type vg_name: string + @ivar vg_name: name of the volume group containing the PV + @type size: float + @ivar size: size of the PV in MiB + @type free: float + @ivar free: free space in the PV, in MiB + @type attributes: string + @ivar attributes: PV attributes + """ + __slots__ = [ + "name", + "vg_name", + "size", + "free", + "attributes", + ] + + def IsEmpty(self): + """Is this PV empty? + + """ + return self.size <= (self.free + 1) + + def IsAllocatable(self): + """Is this PV allocatable? + + """ + return ("a" in self.attributes) diff --git a/lib/utils/__init__.py b/lib/utils/__init__.py index 69cc0a3f9852accb5d975e4ecadc0af0386c2478..d3319c1e2fafa4221f418985225de9d8874eaa95 100644 --- a/lib/utils/__init__.py +++ b/lib/utils/__init__.py @@ -48,6 +48,7 @@ from ganeti.utils.filelock import * from ganeti.utils.hash import * from ganeti.utils.io import * from ganeti.utils.log import * +from ganeti.utils.lvm import * from ganeti.utils.mlock import * from ganeti.utils.nodesetup import * from ganeti.utils.process import * @@ -488,31 +489,6 @@ def StopDaemon(name): return True -def CheckVolumeGroupSize(vglist, vgname, minsize): - """Checks if the volume group list is valid. - - The function will check if a given volume group is in the list of - volume groups and has a minimum size. - - @type vglist: dict - @param vglist: dictionary of volume group names and their size - @type vgname: str - @param vgname: the volume group we should check - @type minsize: int - @param minsize: the minimum size we accept - @rtype: None or str - @return: None for success, otherwise the error message - - """ - vgsize = vglist.get(vgname, None) - if vgsize is None: - return "volume group '%s' missing" % vgname - elif vgsize < minsize: - return ("volume group '%s' too small (%s MiB required, %d MiB found)" % - (vgname, minsize, vgsize)) - return None - - def SplitTime(value): """Splits time as floating point number into a tuple. diff --git a/lib/utils/lvm.py b/lib/utils/lvm.py new file mode 100644 index 0000000000000000000000000000000000000000..02ba70679254d13cc9afea4593d4dee9f08ca040 --- /dev/null +++ b/lib/utils/lvm.py @@ -0,0 +1,47 @@ +# +# + +# Copyright (C) 2006, 2007, 2010, 2011, 2012 Google 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. + +"""Utility functions for LVM. + +""" + +def CheckVolumeGroupSize(vglist, vgname, minsize): + """Checks if the volume group list is valid. + + The function will check if a given volume group is in the list of + volume groups and has a minimum size. + + @type vglist: dict + @param vglist: dictionary of volume group names and their size + @type vgname: str + @param vgname: the volume group we should check + @type minsize: int + @param minsize: the minimum size we accept + @rtype: None or str + @return: None for success, otherwise the error message + + """ + vgsize = vglist.get(vgname, None) + if vgsize is None: + return "volume group '%s' missing" % vgname + elif vgsize < minsize: + return ("volume group '%s' too small (%s MiB required, %d MiB found)" % + (vgname, minsize, vgsize)) + return None