[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[PATCH 4/4] Make singlePV a more useful boolean, clean up _getSinglePV()



Followup again to the "/boot on LVM" commits for s390x.  The feature
request itself is odd, but it's close to a morbid curiosity now as to
whether or not it'll actually work.  The main limitation with /boot on
LVM on s390x is that the logical volume must be tied to a single
physical volume in the volume group.  Combined with the sorting change
commit, the patch does the following:

1) Makes singlePV a boolean attribute on the PartSpec and
   LVMLogicalVolumeDevice objects.

2) Key on the singlePV attribute rather than the mountpoint so the
   support for logical volumes on a single PV is a little more generic.

3) Modify _getSinglePV() to read the output of the vgs(8) command and
   find a PV with enough free space on it for the logical volume we are
   creating.  Once we find one, return it.  If we don't find one, raise
   an ugly exception and explain things to the user.

Now, the big gap here is what happens if there are no physical volumes
of at least 500MB, since we are limiting this singlePV stuff to /boot on
s390x?  The answer is the user can get the exception described in #3 and
do manual partitioning.  I personally have never seen a DASD under 2GB,
but I'm sure it's possible.  I don't really care.  I've fulfilled the
morbid curiosity of this request and know it works.

Related: rhbz#618376
---
 platform.py             |    3 ++-
 storage/devices.py      |   45 +++++++++++++++++++++++++++++++++------------
 storage/errors.py       |    3 +++
 storage/partitioning.py |    3 ++-
 storage/partspec.py     |   14 +++++++++++---
 5 files changed, 51 insertions(+), 17 deletions(-)

diff --git a/platform.py b/platform.py
index 1d38475..8772b57 100644
--- a/platform.py
+++ b/platform.py
@@ -483,7 +483,8 @@ class S390(Platform):
     def setDefaultPartitioning(self):
         """Return the default platform-specific partitioning information."""
         return [PartSpec(mountpoint="/boot", fstype=self.defaultBootFSType, size=500,
-                         weight=self.weight(mountpoint="/boot"), asVol=True)]
+                         weight=self.weight(mountpoint="/boot"), asVol=True,
+                         singlePV=True)]
 
     def weight(self, fstype=None, mountpoint=None):
         if mountpoint and mountpoint == "/boot":
diff --git a/storage/devices.py b/storage/devices.py
index 3852f9c..38f40b3 100644
--- a/storage/devices.py
+++ b/storage/devices.py
@@ -2220,7 +2220,8 @@ class LVMLogicalVolumeDevice(DMDevice):
     def __init__(self, name, vgdev, size=None, uuid=None,
                  stripes=1, logSize=0, snapshotSpace=0,
                  format=None, exists=None, sysfsPath='',
-                 grow=None, maxsize=None, percent=None):
+                 grow=None, maxsize=None, percent=None,
+                 singlePV=False):
         """ Create a LVMLogicalVolumeDevice instance.
 
             Arguments:
@@ -2238,6 +2239,7 @@ class LVMLogicalVolumeDevice(DMDevice):
                 sysfsPath -- sysfs device path
                 format -- a DeviceFormat instance
                 exists -- indicates whether this is an existing device
+                singlePV -- if true, maps this lv to a single pv
 
                 For new (non-existent) LVs only:
 
@@ -2261,6 +2263,7 @@ class LVMLogicalVolumeDevice(DMDevice):
         self.snapshotSpace = snapshotSpace
         self.stripes = stripes
         self.logSize = logSize
+        self.singlePV = singlePV
 
         self.req_grow = None
         self.req_max_size = 0
@@ -2427,16 +2430,35 @@ class LVMLogicalVolumeDevice(DMDevice):
                 log.debug("vg %s teardown failed; continuing" % self.vg.name)
 
     def _getSinglePV(self):
-        pvs = []
-        paths = []
-
-        for parent in self.parents:
-            pvs += parent.parents
-
-        paths = map(lambda x: x.path, pvs)
+        # NOTE: This runs the pvs(8) command and outputs the PV device
+        # name, VG name, and amount of free space on the PV in megabytes.
+        # A change to the Size class will require more handling of the
+        # units switch in this arg list.
+        args = ["pvs", "--noheadings", "--nosuffix", "--aligned",
+                "--units", "M", "-o", "pv_name,vg_name,pv_free"]
+        buf = iutil.execWithCapture("lvm", args, stderr="/dev/tty5")
+
+        for line in buf.splitlines():
+            fields = filter(lambda x: x != '', line.strip().split())
+
+            if len(fields) != 3 or fields[1] != self.vg.name:
+                continue
+            if float(fields[2]) >= self.size:
+                return [fields[0]]             # lvm.lvcreate needs a list
+
+        if hasattr(self.format, "mountpoint"):
+            mountpoint = self.format.mountpoint
+        else:
+            mountpoint = self.format.type
 
-        paths.sort()
-        return paths[-1:]
+        raise SinglePhysicalVolumeError("Single physical volume restriction "
+                                        "for %(mountpoint)s on this platform. "
+                                        "No physical volumes available in "
+                                        "volume group %(vgname)s with "
+                                        "%(size)d MB of available space."
+                                        % {'mountpoint': mountpoint,
+                                           'vgname': self.vg.name,
+                                           'size': self.size})
 
     def create(self, intf=None):
         """ Create the device. """
@@ -2455,8 +2477,7 @@ class LVMLogicalVolumeDevice(DMDevice):
             self.setupParents()
 
             # should we use --zero for safety's sake?
-            if hasattr(self.format, "mountpoint") and \
-               self.format.mountpoint == "/boot":
+            if self.singlePV:
                 lvm.lvcreate(self.vg.name, self._name, self.size, progress=w,
                              pvs=self._getSinglePV())
             else:
diff --git a/storage/errors.py b/storage/errors.py
index ea7e79c..1d1fea0 100644
--- a/storage/errors.py
+++ b/storage/errors.py
@@ -85,6 +85,9 @@ class MDMemberError(DeviceFormatError):
 class PhysicalVolumeError(DeviceFormatError):
     pass
 
+class SinglePhysicalVolumeError(DeviceFormatError):
+    pass
+
 class SwapSpaceError(DeviceFormatError):
     pass
 
diff --git a/storage/partitioning.py b/storage/partitioning.py
index 6fc4924..b99abb9 100644
--- a/storage/partitioning.py
+++ b/storage/partitioning.py
@@ -181,7 +181,8 @@ def _scheduleLVs(anaconda, devs):
                                         mountpoint=request.mountpoint,
                                         grow=request.grow,
                                         maxsize=request.maxSize,
-                                        size=request.size)
+                                        size=request.size,
+                                        singlePV=request.singlePV)
 
         # schedule the device for creation
         anaconda.id.storage.createDevice(dev)
diff --git a/storage/partspec.py b/storage/partspec.py
index 8ad81ca..a126c3f 100644
--- a/storage/partspec.py
+++ b/storage/partspec.py
@@ -21,7 +21,8 @@
 
 class PartSpec(object):
     def __init__(self, mountpoint=None, fstype=None, size=None, maxSize=None,
-                 grow=False, asVol=False, weight=0, requiredSpace=0):
+                 grow=False, asVol=False, singlePV=False, weight=0,
+                 requiredSpace=0):
         """ Create a new storage specification.  These are used to specify
             the default partitioning layout as an object before we have the
             storage system up and running.  The attributes are obvious
@@ -29,6 +30,8 @@ class PartSpec(object):
 
             asVol -- Should this be allocated as a logical volume?  If not,
                      it will be allocated as a partition.
+            singlePV -- Should this logical volume map to a single physical
+                        volume in the volume group?  Implies asVol=True
             weight -- An integer that modifies the sort algorithm for partition
                       requests.  A larger value means the partition will end up
                       closer to the front of the disk.  This is mainly used to
@@ -50,17 +53,22 @@ class PartSpec(object):
         self.maxSize = maxSize
         self.grow = grow
         self.asVol = asVol
+        self.singlePV = singlePV
         self.weight = weight
         self.requiredSpace = requiredSpace
 
+        if self.singlePV and not self.asVol:
+            self.asVol = True
+
     def __str__(self):
         s = ("%(type)s instance (%(id)s) -- \n"
-             "  mountpoint = %(mountpoint)s  asVol = %(asVol)s\n"
+             "  mountpoint = %(mountpoint)s  asVol = %(asVol)s  singlePV = %(singlePV)s\n"
              "  weight = %(weight)s  fstype = %(fstype)s\n"
              "  size = %(size)s  maxSize = %(maxSize)s  grow = %(grow)s\n" %
              {"type": self.__class__.__name__, "id": "%#x" % id(self),
               "mountpoint": self.mountpoint, "asVol": self.asVol,
-              "weight": self.weight, "fstype": self.fstype, "size": self.size,
+              "singlePV": self.singlePV, "weight": self.weight,
+              "fstype": self.fstype, "size": self.size,
               "maxSize": self.maxSize, "grow": self.grow})
 
         return s
-- 
1.7.3.5


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]