From e0b4396247af7ccfee848175db16fe4035144657 Mon Sep 17 00:00:00 2001
From: David Lehman <dlehman@redhat.com>
Date: Fri, 28 Mar 2014 16:19:48 -0500
Subject: [PATCH 07/18] Add backend functions for creating and managing
 snapshots.

---
 blivet/devicelibs/btrfs.py | 20 +++++++++++++++-----
 blivet/devicelibs/lvm.py   | 35 +++++++++++++++++++++++++++++++++++
 2 files changed, 50 insertions(+), 5 deletions(-)

diff --git a/blivet/devicelibs/btrfs.py b/blivet/devicelibs/btrfs.py
index 61697ba..e1d08d0 100644
--- a/blivet/devicelibs/btrfs.py
+++ b/blivet/devicelibs/btrfs.py
@@ -107,20 +107,23 @@ def delete_subvolume(mountpoint, name):
     args = ["subvol", "delete", path]
     return btrfs(args)
 
-_SUBVOL_REGEX_STR = r'ID (\d+) gen \d+ parent (\d+) top level \d+ path (.+)\n'
+_SUBVOL_REGEX_STR = r'ID (?P<id>\d+) gen \d+ (cgen \d+ )?parent (?P<parent>\d+) top level \d+ (otime \d{4}-\d{2}-\d{2} \d\d:\d\d:\d\d )?path (?P<path>.+)\n'
 _SUBVOL_REGEX = re.compile(_SUBVOL_REGEX_STR)
 
 # get a list of subvolumes from a mounted btrfs filesystem
-def list_subvolumes(mountpoint):
+def list_subvolumes(mountpoint, snapshots_only=False):
     if not os.path.ismount(mountpoint):
         raise ValueError("volume not mounted")
 
     args = ["subvol", "list", "-p", mountpoint]
+    if snapshots_only:
+        args.insert(2, "-s")
+
     buf = btrfs(args, capture=True)
     vols = []
-    for group in _SUBVOL_REGEX.findall(buf):
-        vols.append({"id": int(group[0]), "parent": int(group[1]),
-                     "path": group[2]})
+    for m in _SUBVOL_REGEX.finditer(buf):
+        vols.append({"id": int(m.group('id')), "parent": int(m.group('parent')),
+                     "path": m.group('path')})
 
     return vols
 
@@ -138,6 +141,13 @@ def get_default_subvolume(mountpoint):
 
     return default
 
+def create_snapshot(source, dest):
+    if not os.path.ismount(source):
+        raise ValueError("source is not a mounted subvolume")
+
+    args = ["subvol", "snapshot", source, dest]
+    return btrfs(args)
+
 _DEVICE_REGEX_STR = r'devid[ \t]+(\d+)[ \t]+size[ \t]+(\S+)[ \t]+used[ \t]+(\S+)[ \t]+path[ \t]+(\S+)\n'
 _DEVICE_REGEX = re.compile(_DEVICE_REGEX_STR)
 
diff --git a/blivet/devicelibs/lvm.py b/blivet/devicelibs/lvm.py
index f2b85ac..a66f4d6 100644
--- a/blivet/devicelibs/lvm.py
+++ b/blivet/devicelibs/lvm.py
@@ -520,6 +520,32 @@ def lvdeactivate(vg_name, lv_name):
     except LVMError as msg:
         raise LVMError("lvdeactivate failed for %s: %s" % (lv_name, msg))
 
+def lvsnapshotmerge(vg_name, lv_name):
+    """ Merge(/rollback/revert) a snapshot volume into its origin.
+
+        .. note::
+
+            This is an asynchronous procedure. See lvconvert(8) for details of
+            how merge is handled by lvm.
+
+    """
+    args = ["lvconvert", "--merge", "%s/%s" % (vg_name, lv_name)]
+    args += _getConfigArgs() + [lv_name]
+
+    try:
+        lvm(args)
+    except LVMError as msg:
+        raise LVMError("lvsnapshotmerge failed for %s: %s" % (lv_name, msg))
+
+def lvsnapshotcreate(vg_name, lv_name, size, origin):
+    args = ["lvcreate", "-s", "-L", "%dm" % size.convertTo(spec="MiB"),
+            "-n", lv_name] + _getConfigArgs() + ["%s/%s" % (vg_name, origin)]
+
+    try:
+        lvm(args)
+    except LVMError as msg:
+        raise LVMError("lvsnapshotcreate failed for %s/%s: %s" % (vg_name, lv_name, msg))
+
 def thinpoolcreate(vg_name, lv_name, size, metadatasize=None, chunksize=None):
     args = ["lvcreate", "--thinpool", "%s/%s" % (vg_name, lv_name),
             "--size", "%dm" % size.convertTo(spec="mib")]
@@ -550,6 +576,15 @@ def thinlvcreate(vg_name, pool_name, lv_name, size):
     except LVMError as msg:
         raise LVMError("lvcreate failed for %s/%s: %s" % (vg_name, lv_name, msg))
 
+def thinsnapshotcreate(vg_name, lv_name, origin_name, pool_name=None):
+    args = ["lvcreate", "-s", "-n", lv_name, "%s/%s" % (vg_name, origin_name)]
+    if pool_name:
+        args.extend(["--thinpool", pool_name])
+    try:
+        lvm(args)
+    except LVMError as msg:
+        raise LVMError("lvcreate (snapshot) failed for %s/%s: %s" % (vg_name, lv_name, msg))
+
 def thinlvpoolname(vg_name, lv_name):
     args = ["lvs", "--noheadings", "-o", "pool_lv"] + \
             _getConfigArgs(read_only_locking=True) + \
-- 
1.9.0

