From 4c83ecc556bdf83dd4954d0d073a9368a48be33d Mon Sep 17 00:00:00 2001
From: David Lehman <dlehman@redhat.com>
Date: Fri, 28 Mar 2014 16:20:40 -0500
Subject: [PATCH 2/5] Add Device classes for snapshots.

---
 blivet/devices.py | 125 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 121 insertions(+), 4 deletions(-)

diff --git a/blivet/devices.py b/blivet/devices.py
index 77602f1..96a1294 100644
--- a/blivet/devices.py
+++ b/blivet/devices.py
@@ -2979,6 +2979,71 @@ class LVMLogicalVolumeDevice(DMDevice):
         elif data.resize:
             data.size = self.targetSize.convertTo(spec="MiB")
 
+class LVMSnapShotDevice(LVMLogicalVolumeDevice):
+    """ An LVM snapshot """
+    _type = "lvm snapshot"
+
+    def __init__(self, name, parents=None, size=None, uuid=None,
+                 stripes=1, logSize=0,
+                 format=None, exists=False, sysfsPath='',
+                 grow=None, maxsize=None, percent=None,
+                 origin=None, vorigin=False):
+        """ Create a LVMLogicalVolumeDevice instance.
+
+            Arguments:
+
+                name -- the device name (generally a device node's basename)
+                vgdev -- volume group (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
+                origin -- the origin for the snapshot (LVMLogicalVolumeDevice)
+                vorigin -- is this a vorigin snapshot?
+
+                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
+
+        """
+        self.origin = origin
+        self.vorigin = vorigin
+        if not self.origin and not self.vorigin:
+            raise DeviceError("lvm snapshot devices require an origin lv")
+
+        if vorigin and not exists:
+            raise DeviceError("only existing vorigin snapshots are supported")
+
+        # XXX what about stripes, logSize?
+        # XXX percent of what? vg or origin?
+        super(LVMSnapShotDevice, self).__init__(name, parents=parents,
+                                                size=size, uuid=uuid,
+                                                format=format, exists=exists,
+                                                sysfsPath=sysfsPath,
+                                                grow=grow, maxsize=maxsize,
+                                                percent=percent)
+
+    def _create(self):
+        """ Create the device. """
+        log_method_call(self, self.name, status=self.status)
+        lvm.lvsnapshotcreate(self.vg.name, self._name, self.size,
+                             self.origin.lvname)
+
+    def merge(self):
+        log_method_call(self, self.name, status=self.status)
+        self.vg.setup()    # orig=True?
+        self.origin.teardown()
+        self.teardown()
+
+        udev_settle()
+        lvm.lvsnapshotmerge(self.vg.name, self.lvname)
+
 class LVMThinPoolDevice(LVMLogicalVolumeDevice):
     """ An LVM Thin Pool """
     _type = "lvmthinpool"
@@ -3142,6 +3207,30 @@ class LVMThinLogicalVolumeDevice(LVMLogicalVolumeDevice):
         data.thin_volume = True
         data.pool_name = self.pool.lvname
 
+class LVMThinSnapShotDevice(LVMThinLogicalVolumeDevice):
+    """ An LVM Thin Snapshot """
+    _type = "lvmthinsnapshot"
+    _resizable = False
+
+    def __init__(self, *args, **kwargs):
+        self.origin = kwargs.pop("origin", None)
+        if not self.origin:
+            raise DeviceError("lvm snapshot devices require an origin lv")
+
+        super(LVMThinSnapShotDevice, self).__init__(*args, **kwargs)
+
+    def _create(self):
+        """ Create the device. """
+        log_method_call(self, self.name, status=self.status)
+        pool = None
+        if not isinstance(self.origin, LVMThinLogicalVolumeDevice):
+            # if the origin is not a thin volume we need to tell lvm which pool
+            # to use
+            pool_name = self.pool.lvname
+
+        lvm.thinsnapshotcreate(self.vg.name, self._name, self.origin.lvname,
+                               pool_name=pool_name)
+
 class MDRaidArrayDevice(ContainerDevice):
     """ An mdraid (Linux RAID) device. """
     _type = "mdarray"
@@ -4675,7 +4764,7 @@ class BTRFSVolumeDevice(BTRFSDevice, ContainerDevice):
         names = [v.name for v in self.subvolumes]
         self.subvolumes.pop(names.index(name))
 
-    def listSubVolumes(self):
+    def listSubVolumes(self, snapshotsOnly=False):
         subvols = []
         if flags.installer_mode:
             self.setup(orig=True)
@@ -4687,7 +4776,8 @@ class BTRFSVolumeDevice(BTRFSDevice, ContainerDevice):
             return subvols
 
         try:
-            subvols = btrfs.list_subvolumes(self.originalFormat._mountpoint)
+            subvols = btrfs.list_subvolumes(self.originalFormat._mountpoint,
+                                            snapshots_only=snapshotsOnly)
         except errors.BTRFSError as e:
             log.debug("failed to list subvolumes: %s", e)
         else:
@@ -4845,8 +4935,10 @@ class BTRFSSubVolumeDevice(BTRFSDevice):
         if not mountpoint:
             raise RuntimeError("btrfs subvol create requires mounted volume")
 
-        btrfs.create_subvolume(mountpoint, self.name)
-        self.volume._undo_temp_mount()
+        try:
+            btrfs.create_subvolume(mountpoint, self.name)
+        finally:
+            self.volume._undo_temp_mount()
 
     def _postCreate(self):
         super(BTRFSSubVolumeDevice, self)._postCreate()
@@ -4866,3 +4958,28 @@ class BTRFSSubVolumeDevice(BTRFSDevice):
         data.subvol = True
         data.name = self.name
         data.preexist = self.exists
+
+class BTRFSSnapShotDevice(BTRFSSubVolumeDevice):
+    """ A btrfs snapshot pseudo-device. """
+    _type = "btrfs snapshot"
+
+    def __init__(self, *args, **kwargs):
+        self.source = kwargs.pop("source")
+        if not kwargs.get("exists", False) and not self.source:
+            raise ValueError("non-existent btrfs snapshots must have a source")
+
+        super(BTRFSSnapShotDevice, self).__init__(*args, **kwargs)
+
+    def _create(self):
+        log_method_call(self, self.name, status=self.status)
+        self.volume._do_temp_mount()
+        mountpoint = self.volume.format._mountpoint
+        if not mountpoint:
+            raise RuntimeError("btrfs subvol create requires mounted volume")
+
+        source_path = "%s/%s" % (mountpoint, self.source.name)
+        dest_path = "%s/%s" % (mountpoint, self.name)
+        try:
+            btrfs.create_snapshot(source_path, dest_path)
+        finally:
+            self.volume._undo_temp_mount()
-- 
1.9.0

