Skip to content

Commit

Permalink
Create and use internal LVs instead of static values
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
vpodzime committed Jun 18, 2015
1 parent e46eca8 commit 1f07ec0
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 53 deletions.
33 changes: 33 additions & 0 deletions blivet/devicelibs/lvm.py
Expand Up @@ -20,6 +20,8 @@
# Author(s): Dave Lehman <dlehman@redhat.com>
#

import re

from collections import namedtuple
from gi.repository import BlockDev as blockdev

Expand Down Expand Up @@ -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
38 changes: 32 additions & 6 deletions blivet/devices/lvm.py
Expand Up @@ -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 """
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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):
Expand All @@ -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"
Expand Down Expand Up @@ -1282,7 +1308,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 = []
Expand Down
126 changes: 79 additions & 47 deletions blivet/populator.py
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand All @@ -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:
Expand Down Expand Up @@ -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
Expand All @@ -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:
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 1f07ec0

Please sign in to comment.