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