From 67fc4c7d7070713b1e6a613642f95810d58518c6 Mon Sep 17 00:00:00 2001
From: David Lehman <dlehman@redhat.com>
Date: Wed, 22 May 2013 13:09:23 -0500
Subject: [PATCH 05/10] Add classes for lvm thin pool and thin volume.

---
 blivet/devices.py | 213 +++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 196 insertions(+), 17 deletions(-)

diff --git a/blivet/devices.py b/blivet/devices.py
index 2bb7289..441f146 100644
--- a/blivet/devices.py
+++ b/blivet/devices.py
@@ -786,6 +786,7 @@ class StorageDevice(Device):
         try:
             self._create()
         except Exception as e:
+            raise
             raise DeviceCreateError(str(e), self.name)
         else:
             self._postCreate()
@@ -2228,11 +2229,16 @@ 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))
         self._lvs.append(lv)
+        if hasattr(lv, "pool") and lv.pool in self.lvs:
+            lv.pool._addLogVol(lv)
 
     def _removeLogVol(self, lv):
         """ Remove an LV from this VG. """
@@ -2240,6 +2246,8 @@ class LVMVolumeGroupDevice(DMDevice):
             raise ValueError("specified lv is not part of this vg")
 
         self._lvs.remove(lv)
+        if hasattr(lv, "pool") and lv.pool in self.lvs:
+            lv.pool._removeLogVol(lv)
 
     def _addPV(self, pv):
         """ Add a PV to this VG. """
@@ -2378,6 +2386,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.
@@ -2426,13 +2442,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)
@@ -2474,7 +2491,7 @@ class LVMLogicalVolumeDevice(DMDevice):
                 raise SinglePhysicalVolumeError(self.singlePVerr)
 
         # here we go with the circular references
-        self.vg._addLogVol(self)
+        self.container._addLogVol(self)
 
     def __repr__(self):
         s = DMDevice.__repr__(self)
@@ -2533,6 +2550,11 @@ class LVMLogicalVolumeDevice(DMDevice):
         return self.parents[0]
 
     @property
+    def container(self):
+        """ This Logical Volume's container (thin pool or vg). """
+        return self.parents[0]
+
+    @property
     def mapName(self):
         """ This device's device-mapper map name """
         # Thank you lvm for this lovely hack.
@@ -2625,15 +2647,6 @@ class LVMLogicalVolumeDevice(DMDevice):
     def resize(self):
         log_method_call(self, self.name, status=self.status)
         self._preDestroy()
-
-        # Setup VG parents (in case they are dmraid partitions for example)
-        self.vg.setupParents(orig=True)
-
-        if self.originalFormat.exists:
-            self.originalFormat.teardown()
-        if self.format.exists:
-            self.format.teardown()
-
         udev_settle()
         lvm.lvresize(self.vg.name, self._name, self.size)
 
@@ -2661,6 +2674,172 @@ class LVMLogicalVolumeDevice(DMDevice):
             return -1
         return 0
 
+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):
+        """ 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
+
+        """
+        # thin pool checks go here
+
+        super(LVMThinPoolDevice, self).__init__(name, parents=parents,
+                                                size=size, uuid=uuid,
+                                                format=format, exists=exists,
+                                                sysfsPath=sysfsPath, grow=grow,
+                                                maxsize=maxsize,
+                                                percent=percent)
+
+        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?
+
+        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)
+
+    @property
+    def lvs(self):
+        """ A list of this pool's LVs """
+        return self._lvs[:]     # we don't want folks changing our list
+
+    def _setSize(self, size):
+        """
+        bs = self.vg.peSize * 20
+        if size % bs != 0:
+            size = ((size / bs) + 1) * bs
+            log.debug("aligning new pool size to a multiple of %d (%d)"
+                        % (bs, size))
+        """
+
+        super(LVMThinPoolDevice, self)._setSize(size)
+
+    size = property(StorageDevice._getSize, _setSize)
+
+    @property
+    def vgSpaceUsed(self):
+        space = super(LVMThinPoolDevice, self).vgSpaceUsed
+        space += self.vg.peSize * len(self.lvs)
+        space += self.vg.align(10 * len(self.vg.pvs), roundup=True)
+        return space
+
+    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)
+
+    @property
+    def isleaf(self):
+        return len(self._lvs) == 0 and super(LVMThinPoolDevice, self).isleaf
+
+
+class LVMThinLogicalVolumeDevice(LVMLogicalVolumeDevice):
+    """ An LVM Thin Logical Volume """
+    _type = "lvmthinlv"
+    _resizable = True
+
+    def __init__(self, name, parents=None, size=None, uuid=None,
+                 format=None, exists=False, sysfsPath='', singlePV=False,
+                 grow=None, maxsize=None, percent=None):
+        """ Create a LVMThinLogicalVolumeDevice instance.
+
+            Arguments:
+
+                name -- the device name (generally a device node's basename)
+                parents -- thin pool (LVMThinPoolDevice 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
+
+        """
+        # thin pool checks go here
+
+        # XXX Should the pool be the parent, or should the vg?
+        self.pool = parents[0]
+        parents = self.pool.parents
+
+        super(LVMThinLogicalVolumeDevice, self).__init__(name, parents=parents,
+                                                size=size, uuid=uuid,
+                                                format=format, exists=exists,
+                                                sysfsPath=sysfsPath, grow=grow,
+                                                maxsize=maxsize,
+                                                percent=percent)
+
+    def dependsOn(self, dep):
+        """ Return True if this device depends on dep. """
+        return (self.pool == dep or Device.dependsOn(self, dep))
+
+    @property
+    def vgSpaceUsed(self):
+        # FIXME: at least account for used space in existing vols
+        #return self.vg.align(1, roundup=True)
+        return 0
+
+    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 dracutSetupArgs(self):
+    #    # Note no mapName usage here, this is a lvm cmdline name, which
+    #    # is different (ofcourse)
+    #    return set(["rd.lvm.lv=%s/%s" % (self.vg.name, self.pool._name),
+    #                "rd.lvm.lv=%s/%s" % (self.vg.name, self._name)])
+
 class MDRaidArrayDevice(StorageDevice):
     """ An mdraid (Linux RAID) device. """
     _type = "mdarray"
-- 
1.8.1.4

