[master 5/9] Create and use internal LVs instead of static values

vpodzime installerbot-noreply at redhat.com
Mon Jun 8 14:13:38 UTC 2015


From: Vratislav Podzimek <vpodzime at redhat.com>

Instead of calculating static values for number of copies, log size and metadata
size of a given LV, we should use internal LVs assigned to their "parent"
(normal) LVs and calculate such values dynamically from those internal LVs.
---
 blivet/devicelibs/lvm.py |  33 +++++++++++++
 blivet/devices/lvm.py    |  38 +++++++++++---
 blivet/populator.py      | 126 +++++++++++++++++++++++++++++------------------
 3 files changed, 144 insertions(+), 53 deletions(-)

diff --git a/blivet/devicelibs/lvm.py b/blivet/devicelibs/lvm.py
index 0b7658c..57aa740 100644
--- a/blivet/devicelibs/lvm.py
+++ b/blivet/devicelibs/lvm.py
@@ -20,6 +20,8 @@
 # Author(s): Dave Lehman <dlehman at redhat.com>
 #
 
+import re
+
 from collections import namedtuple
 from gi.repository import BlockDev as blockdev
 
@@ -115,3 +117,34 @@ def lvm_cc_removeFilterRejectRegexp(regexp):
 def lvm_cc_resetFilter():
     config_args_data["filterRejects"] = []
     config_args_data["filterAccepts"] = []
+
+def determine_parent_lv(vg_name, internal_lv, lvs):
+    """Try to determine which of the lvs is the parent of the internal_lv
+
+    :param str vg_name: name of the VG the internal_lv and lvs belong to
+    :type internal_lv: :class:`~.devices.lvm.LMVInternalLogicalVolumeDevice`
+    :type lvs: :class:`~.devices.lvm.LMVLogicalVolumeDevice`
+
+    """
+    for lv in lvs:
+        if internal_lv.lvname == lv.lvname:
+            # skip the internal_lv itself
+            continue
+
+        # check if the lv's name is the name of the internal LV without the suffix
+        # e.g. 'pool' and 'pool_tmeta'
+        if re.match(lv.lvname+internal_lv.name_suffix+"$", internal_lv.lvname):
+            return lv
+
+        # cache pools are internal LVs of cached LVs
+        try:
+            pool_name = blockdev.lvm.cache_pool_name(vg_name, lv.lvname)
+            if pool_name == internal_lv.lvname:
+                return lv
+        except blockdev.LVMError:
+            # doesn't have a pool
+            pass
+
+        # TODO: use 'data_lv' and 'metadata_lv' on appropriate internal LVs
+
+    return None
diff --git a/blivet/devices/lvm.py b/blivet/devices/lvm.py
index 595a7ae..3d6b3d6 100644
--- a/blivet/devices/lvm.py
+++ b/blivet/devices/lvm.py
@@ -48,7 +48,15 @@
 from .dm import DMDevice
 from .md import MDRaidArrayDevice
 
-INTERNAL_LV_CLASSES = []
+_INTERNAL_LV_CLASSES = []
+
+def get_internal_lv_class(lv_attr):
+    # XXX: need to do some heuristic on the LV name?
+    for cls in _INTERNAL_LV_CLASSES:
+        if lv_attr[0] in cls.attr_letters:
+            return cls
+
+    return None
 
 class LVMVolumeGroupDevice(ContainerDevice):
     """ An LVM Volume Group """
@@ -452,10 +460,9 @@ class LVMLogicalVolumeDevice(DMDevice):
     _containerClass = LVMVolumeGroupDevice
     _external_dependencies = [availability.BLOCKDEV_LVM_PLUGIN]
 
-    def __init__(self, name, parents=None, size=None, uuid=None,
-                 copies=1, logSize=None, segType=None,
-                 fmt=None, exists=False, sysfsPath='',
-                 grow=None, maxsize=None, percent=None):
+    def __init__(self, name, parents=None, size=None, uuid=None, segType=None,
+                 fmt=None, exists=False, sysfsPath='', grow=None, maxsize=None,
+                 percent=None):
         """
             :param name: the device name (generally a device node's basename)
             :type name: str
@@ -516,6 +523,7 @@ def __init__(self, name, parents=None, size=None, uuid=None,
         self._check_parents()
         self._add_to_parents()
 
+        self._metaDataSize = Size(0)
         self._internal_lvs = []
 
     def _check_parents(self):
@@ -538,6 +546,24 @@ def _add_to_parents(self):
         # a normal LV has only exactly parent -- the VG it belongs to
         self._parents[0]._addLogVol(self)
 
+    @property
+    def copies(self):
+        image_lvs = [int_lv for int_lv in self._internal_lvs if isinstance(int_lv, LVMImageLogicalVolumeDevice)]
+        return len(image_lvs) or 1
+
+    @property
+    def logSize(self):
+        log_lvs = (int_lv for int_lv in self._internal_lvs if isinstance(int_lv, LVMLogLogicalVolumeDevice))
+        return sum(lv.size for lv in log_lvs)
+
+    @property
+    def metaDataSize(self):
+        if self._metaDataSize:
+            return self._metaDataSize
+
+        md_lvs = (int_lv for int_lv in self._internal_lvs if isinstance(int_lv, LVMMetadataLogicalVolumeDevice))
+        return sum(lv.size for lv in md_lvs)
+
     def __repr__(self):
         s = DMDevice.__repr__(self)
         s += ("  VG device = %(vgdev)r\n"
@@ -1285,7 +1311,7 @@ def __init__(self, name, parents=None, size=None, uuid=None,
                                                 percent=percent,
                                                 segType=segType)
 
-        self.metaDataSize = metadatasize or Size(0)
+        self._metaDataSize = metadatasize or Size(0)
         self.chunkSize = chunksize or Size(0)
         self.profile = profile
         self._lvs = []
diff --git a/blivet/populator.py b/blivet/populator.py
index 9514afc..d86fa39 100644
--- a/blivet/populator.py
+++ b/blivet/populator.py
@@ -41,6 +41,7 @@
 from .devices import MultipathDevice, OpticalDevice
 from .devices import PartitionDevice, ZFCPDiskDevice, iScsiDiskDevice
 from .devices import devicePathToName
+from .devices.lvm import get_internal_lv_class
 from . import formats
 from .devicelibs import lvm
 from .devicelibs import raid
@@ -863,6 +864,9 @@ def handleVgLvs(self, vg_device):
             log.debug("no LVs listed for VG %s", vg_name)
             return
 
+        all_lvs = []
+        internal_lvs = []
+
         def addRequiredLV(name, msg):
             """ Add a prerequisite/parent LV.
 
@@ -879,7 +883,9 @@ def addRequiredLV(name, msg):
             """
             vol = self.getDeviceByName(name)
             if vol is None:
-                addLV(lv_info[name])
+                new_lv = addLV(lv_info[name])
+                if new_lv:
+                    all_lvs.append(new_lv)
                 vol = self.getDeviceByName(name)
 
                 if vol is None:
@@ -926,33 +932,11 @@ def addLV(lv):
             elif lv_attr[0] == 'v':
                 # skip vorigins
                 return
-            elif lv_attr[0] in 'Ii':
-                # mirror image
-                rname = re.sub(r'_[rm]image.+', '', lv_name[1:-1])
-                name = "%s-%s" % (vg_name, rname)
-                addRequiredLV(name, "failed to look up raid lv")
-                raid_items[name]["copies"] += 1
-                return
-            elif lv_attr[0] == 'e':
-                if lv_name.endswith("_pmspare]"):
-                    # spare metadata area for any thin pool that needs repair
-                    return
-                elif lv_name.endswith("_cmeta]"):
-                    # cache metadata volume (skip, we ignore cache volumes)
-                    return
-
-                # raid metadata volume
-                lv_name = re.sub(r'_[tr]meta.*', '', lv_name[1:-1])
-                name = "%s-%s" % (vg_name, lv_name)
-                addRequiredLV(name, "failed to look up raid lv")
-                raid_items[name]["meta"] += lv_size
-                return
-            elif lv_attr[0] == 'l':
-                # log volume
-                rname = re.sub(r'_mlog.*', '', lv_name[1:-1])
-                name = "%s-%s" % (vg_name, rname)
-                addRequiredLV(name, "failed to look up log lv")
-                raid_items[name]["log"] = lv_size
+            elif lv_attr[0] in 'IielTCo' and lv_name.endswith(']'):
+                # an internal LV, add the an instance of the appropriate class
+                # to internal_lvs for later processing when non-internal LVs are
+                # processed
+                internal_lvs.append(lv_name)
                 return
             elif lv_attr[0] == 't':
                 # thin pool
@@ -975,7 +959,7 @@ def addLV(lv):
 
                 lv_parents = [self.getDeviceByName(pool_device_name)]
             elif lv_name.endswith(']'):
-                # Internal LVM2 device
+                # unrecognized Internal LVM2 device
                 return
             elif lv_attr[0] not in '-mMrRoOC':
                 # Ignore anything else except for the following:
@@ -1004,30 +988,78 @@ def addLV(lv):
                     lv_info = udev.get_device(lv_device.sysfsPath)
                     if not lv_info:
                         log.error("failed to get udev data for lv %s", lv_device.name)
-                        return
+                        return lv_device
 
                     # do format handling now
                     self.addUdevDevice(lv_info)
 
-        raid_items = dict((n.replace("[", "").replace("]", ""),
-                     {"copies": 0, "log": Size(0), "meta": Size(0)})
-                     for n in lv_info.keys())
-        for lv in lv_info.values():
-            addLV(lv)
+                return lv_device
 
-        for name, data in raid_items.items():
-            lv_dev = self.getDeviceByName(name)
-            if not lv_dev:
-                # hidden lv, eg: pool00_tdata
-                continue
+            return None
+
+        def createInternalLV(lv):
+            lv_name = lv.lv_name
+            lv_uuid = lv.uuid
+            lv_attr = lv.attr
+            lv_size = Size(lv.size)
+            lv_type = lv.segtype
+
+            matching_cls = get_internal_lv_class(lv_attr)
+            if matching_cls is None:
+                raise DeviceTreeError("No internal LV class supported for type '%s'" % lv_attr[0])
+
+            # strip the "[]"s marking the LV as internal
+            lv_name = lv_name.strip("[]")
+
+            # don't know the parent LV yet, will be set later
+            new_lv = matching_cls(lv_name, vg_device, parent_lv=None, size=lv_size, uuid=lv_uuid, exists=True, segType=lv_type)
+            if new_lv.status:
+                new_lv.updateSysfsPath()
+                new_lv.updateSize()
 
-            lv_dev.copies = data["copies"] or 1
-            lv_dev.metaDataSize = data["meta"]
-            lv_dev.logSize = data["log"]
-            log.debug("set %s copies to %d, metadata size to %s, log size "
-                      "to %s, total size %s",
-                        lv_dev.name, lv_dev.copies, lv_dev.metaDataSize,
-                        lv_dev.logSize, lv_dev.vgSpaceUsed)
+                lv_info = udev.get_device(new_lv.sysfsPath)
+                if not lv_info:
+                    log.error("failed to get udev data for lv %s", new_lv.name)
+                    return new_lv
+
+            return new_lv
+
+        # add LVs
+        for lv in lv_info.values():
+            # add the LV to the DeviceTree
+            new_lv = addLV(lv)
+
+            if new_lv:
+                # save the reference for later use
+                all_lvs.append(new_lv)
+
+        # Instead of doing a topological sort on internal LVs to make sure the
+        # parent LV is always created before its internal LVs (an internal LV
+        # can have internal LVs), we just create all the instances here and
+        # assign their parents later. Those who are not assinged a parent (which
+        # would hold a reference to them) will get eaten by the garbage
+        # collector.
+
+        # create device instances for the internal LVs
+        orphan_lvs = dict()
+        for lv_name in internal_lvs:
+            full_name = "%s-%s" % (vg_name, lv_name)
+            try:
+                new_lv = createInternalLV(lv_info[full_name])
+            except DeviceTreeError as e:
+                log.warning("Failed to process an internal LV '%s': %s", full_name, e)
+            else:
+                orphan_lvs[full_name] = new_lv
+                all_lvs.append(new_lv)
+
+        # assign parents to internal LVs (and vice versa, see
+        # :class:`~.devices.lvm.LVMInternalLogicalVolumeDevice`)
+        for lv in orphan_lvs.values():
+            parent_lv = lvm.determine_parent_lv(vg_name, lv, all_lvs)
+            if parent_lv:
+                lv.parent_lv = parent_lv
+            else:
+                log.warning("Failed to determine parent LV for an internal LV '%s'", lv.name)
 
     def handleUdevLVMPVFormat(self, info, device):
         # pylint: disable=unused-argument


-- 
To view this commit on github, visit https://github.com/rhinstaller/blivet/commit/d23aae4fadb82e0bfdab10fcba002234c9059884


More information about the anaconda-patches mailing list