[master 5/12] Create and use internal LVs instead of static values
vpodzime
installerbot-noreply at redhat.com
Fri Jun 12 13:58:05 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