[PATCH 06/10] Add classes for lvm thin pool and thin volume.

David Lehman dlehman at redhat.com
Tue Jun 25 20:37:56 UTC 2013


This is another layer of lvm that provides thinly-provisioned volumes.
A thin pool is a special kind of lv that is a container for thinly-
provisioned lvs. The sizes of the thin lvs are not constrained by the
size of the pool that contains them except at the point where they
actually contain/use that much data/space.
---
 blivet/devices.py    | 176 ++++++++++++++++++++++++++++++++++++++++++++++++---
 blivet/devicetree.py |   2 +
 2 files changed, 170 insertions(+), 8 deletions(-)

diff --git a/blivet/devices.py b/blivet/devices.py
index c3fc9be..e699e54 100644
--- a/blivet/devices.py
+++ b/blivet/devices.py
@@ -2272,7 +2272,10 @@ class LVMVolumeGroupDevice(DMDevice):
 
         # verify we have the space, then add it
         # do not verify for growing vg (because of ks)
-        if not lv.exists and not self.growable and lv.size > self.freeSpace:
+        # FIXME: add a "isthin" property and/or "ispool"?
+        if not lv.exists and not self.growable and \
+           not isinstance(lv, LVMThinLogicalVolumeDevice) and \
+           lv.size > self.freeSpace:
             raise DeviceError("new lv is too large to fit in free space", self.name)
 
         log.debug("Adding %s/%dMB to %s" % (lv.name, lv.size, self.name))
@@ -2422,6 +2425,14 @@ class LVMVolumeGroupDevice(DMDevice):
         return self._lvs[:]     # we don't want folks changing our list
 
     @property
+    def thinpools(self):
+        return [l for l in self._lvs if isinstance(l, LVMThinPoolDevice)]
+
+    @property
+    def thinlvs(self):
+        return [l for l in self._lvs if isinstance(l, LVMThinLogicalVolumeDevice)]
+
+    @property
     def complete(self):
         """Check if the vg has all its pvs in the system
         Return True if complete.
@@ -2480,13 +2491,14 @@ class LVMLogicalVolumeDevice(DMDevice):
                     percent -- percent of VG space to take
 
         """
-        if isinstance(parents, list):
-            if len(parents) != 1:
-                raise ValueError("constructor requires a single LVMVolumeGroupDevice instance")
-            elif not isinstance(parents[0], LVMVolumeGroupDevice):
+        if self.__class__.__name__ == "LVMLogicalVolumeDevice":
+            if isinstance(parents, list):
+                if len(parents) != 1:
+                    raise ValueError("constructor requires a single LVMVolumeGroupDevice instance")
+                elif not isinstance(parents[0], LVMVolumeGroupDevice):
+                    raise ValueError("constructor requires a LVMVolumeGroupDevice instance")
+            elif not isinstance(parents, LVMVolumeGroupDevice):
                 raise ValueError("constructor requires a LVMVolumeGroupDevice instance")
-        elif not isinstance(parents, LVMVolumeGroupDevice):
-            raise ValueError("constructor requires a LVMVolumeGroupDevice instance")
         DMDevice.__init__(self, name, size=size, format=format,
                           sysfsPath=sysfsPath, parents=parents,
                           exists=exists)
@@ -2528,7 +2540,7 @@ class LVMLogicalVolumeDevice(DMDevice):
                 raise SinglePhysicalVolumeError(self.singlePVerr)
 
         # here we go with the circular references
-        self.vg._addLogVol(self)
+        self.parents[0]._addLogVol(self)
 
     def __repr__(self):
         s = DMDevice.__repr__(self)
@@ -2734,6 +2746,154 @@ class LVMLogicalVolumeDevice(DMDevice):
         elif data.resize:
             data.size = int(self.targetSize)
 
+class LVMThinPoolDevice(LVMLogicalVolumeDevice):
+    """ An LVM Thin Pool """
+    _type = "lvmthinpool"
+    _resizable = False
+
+    def __init__(self, name, parents=None, size=None, uuid=None,
+                 format=None, exists=False, sysfsPath='',
+                 grow=None, maxsize=None, percent=None,
+                 metadatasize=None, chunksize=None):
+        """ Create a LVMLogicalVolumeDevice instance.
+
+            Arguments:
+
+                name -- the device name (generally a device node's basename)
+                parents -- vg (LVMVolumeGroupDevice instance)
+
+            Keyword Arguments:
+
+                size -- the device's size (in MB)
+                uuid -- the device's UUID
+                sysfsPath -- sysfs device path
+                format -- a DeviceFormat instance
+                exists -- indicates whether this is an existing device
+
+                For new (non-existent) LVs only:
+
+                    grow -- whether to grow this LV
+                    maxsize -- maximum size for growable LV (in MB)
+                    percent -- percent of VG space to take
+                    metadatasize -- the size of the metadata LV (in MB)
+                    chunksize -- chunk size for the pool (in MB)
+
+        """
+        if metadatasize is not None and \
+           not lvm.is_valid_thin_pool_metadata_size(metadatasize):
+            raise ValueError("invalid metadatasize value")
+
+        if chunksize is not None and \
+           not lvm.is_valid_thin_pool_chunk_size(chunksize):
+            raise ValueError("invalid chunksize value")
+
+        super(LVMThinPoolDevice, self).__init__(name, parents=parents,
+                                                size=size, uuid=uuid,
+                                                format=format, exists=exists,
+                                                sysfsPath=sysfsPath, grow=grow,
+                                                maxsize=maxsize,
+                                                percent=percent)
+
+        self.metaDataSize = metadatasize
+        self.chunkSize = chunksize
+        self._lvs = []
+
+    def _addLogVol(self, lv):
+        """ Add an LV to this pool. """
+        if lv in self._lvs:
+            raise ValueError("lv is already part of this vg")
+
+        # TODO: add some checking to prevent overcommit for preexisting
+        self.vg._addLogVol(lv)
+        log.debug("Adding %s/%dMB to %s" % (lv.name, lv.size, self.name))
+        self._lvs.append(lv)
+
+    def _removeLogVol(self, lv):
+        """ Remove an LV from this pool. """
+        if lv not in self._lvs:
+            raise ValueError("specified lv is not part of this vg")
+
+        self._lvs.remove(lv)
+        self.vg._removeLogVol(lv)
+
+    @property
+    def lvs(self):
+        """ A list of this pool's LVs """
+        return self._lvs[:]     # we don't want folks changing our list
+
+    @property
+    def vgSpaceUsed(self):
+        space = super(LVMThinPoolDevice, self).vgSpaceUsed
+        space += lvm.get_pool_padding(space, pesize=self.vg.peSize)
+        return space
+
+    @property
+    def usedSpace(self):
+        return sum(l.poolSpaceUsed for l in self.lvs)
+
+    @property
+    def freeSpace(self):
+        return self.size - self.usedSpace
+
+    def _create(self):
+        """ Create the device. """
+        log_method_call(self, self.name, status=self.status)
+        # TODO: chunk size, data/metadata split --> profile
+        lvm.thinpoolcreate(self.vg.name, self.lvname, self.size,
+                           metadatasize=self.metaDataSize,
+                           chunksize=self.chunkSize)
+
+    def populateKSData(self, data):
+        super(LVMThinPoolDevice, self).populateKSData(data)
+        data.thin_pool = True
+        data.metadata_size = self.metaDataSize
+        data.chunk_size = self.chunkSize
+
+class LVMThinLogicalVolumeDevice(LVMLogicalVolumeDevice):
+    """ An LVM Thin Logical Volume """
+    _type = "lvmthinlv"
+    _resizable = True
+
+    @property
+    def pool(self):
+        return self.parents[0]
+
+    @property
+    def vg(self):
+        return self.pool.vg
+
+    @property
+    def poolSpaceUsed(self):
+        """ The total space used within the thin pool by this volume.
+
+            This should probably align to the greater of vg extent size and
+            pool chunk size. If it ends up causing overcommit in the amount of
+            less than one chunk per thin lv, so be it.
+        """
+        return self.vg.align(self.size, roundup=True)
+
+    @property
+    def vgSpaceUsed(self):
+        return 0    # the pool's size is already accounted for in the vg
+
+    def _setSize(self, size):
+        size = self.vg.align(util.numeric_type(size))
+        self._size = size
+        self.targetSize = size
+
+    size = property(StorageDevice._getSize, _setSize)
+
+    def _create(self):
+        """ Create the device. """
+        log_method_call(self, self.name, status=self.status)
+        lvm.thinlvcreate(self.vg.name, self.pool.lvname, self.lvname,
+                         self.size)
+
+    def populateKSData(self, data):
+        super(LVMThinLogicalVolumeDevice, self).populateKSData(data)
+        data.thin_volume = True
+        data.pool_name = self.pool.lvname
+
 class MDRaidArrayDevice(StorageDevice):
     """ An mdraid (Linux RAID) device. """
     _type = "mdarray"
diff --git a/blivet/devicetree.py b/blivet/devicetree.py
index 26df985..3f1cba1 100644
--- a/blivet/devicetree.py
+++ b/blivet/devicetree.py
@@ -308,6 +308,8 @@ class DeviceTree(object):
                 if isinstance(device, PartitionDevice) and \
                    device.disk == dev.disk:
                     device.updateName()
+        elif hasattr(dev, "pool"):
+            dev.pool._removeLogVol(dev)
         elif hasattr(dev, "vg"):
             dev.vg._removeLogVol(dev)
         elif hasattr(dev, "volume"):
-- 
1.8.1.4



More information about the anaconda-patches mailing list