diff --git a/blivet/devicelibs/btrfs.py b/blivet/devicelibs/btrfs.py
index a2d4150..5a065fb 100644
--- a/blivet/devicelibs/btrfs.py
+++ b/blivet/devicelibs/btrfs.py
@@ -101,7 +101,11 @@ def delete_subvolume(mountpoint, name):
     return btrfs(args)
 
 def create_snapshot(source, dest):
-    pass
+    if not os.path.ismount(source):
+        raise ValueError("source is not a mounted subvolume")
+
+    args = ["subvol", "snapshot", source, dest]
+    return btrfs(args)
 
 def scan_device(path):
     return btrfs(["device", "scan", path])
diff --git a/blivet/devicelibs/lvm.py b/blivet/devicelibs/lvm.py
index 12d0f7d..7845c0a 100644
--- a/blivet/devicelibs/lvm.py
+++ b/blivet/devicelibs/lvm.py
@@ -412,12 +412,29 @@ def lvorigin(vg_name, lv_name):
 
     return origin
 
+def lvsnapshotmerge(vg_name, lv_name):
+    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, "-n", lv_name]
+    args += _getConfigArgs()
+    args += ["%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 lvcreate(vg_name, lv_name, size, pvs=[]):
-    args = ["lvcreate"] + \
-            ["-L", "%dm" % size] + \
-            ["-n", lv_name] + \
-            _getConfigArgs() + \
-            [vg_name] + pvs
+    args = ["lvcreate", "-L", "%dm" % size, "-n", lv_name]
+    args += _getConfigArgs()
+    args += [vg_name] + pvs
 
     try:
         lvm(args)
@@ -495,6 +512,13 @@ 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):
+    args = ["lvcreate", "-s", "-n", lv_name, "%s/%s" % (vg_name, origin_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) + \
diff --git a/blivet/devices.py b/blivet/devices.py
index 3e2c943..9ff8ac6 100644
--- a/blivet/devices.py
+++ b/blivet/devices.py
@@ -2787,6 +2787,67 @@ class LVMLogicalVolumeDevice(DMDevice):
         elif data.resize:
             data.size = int(self.targetSize)
 
+class LVMSnapShotDevice(LVMLogicalVolumeDevice):
+    """ An LVM snapshot """
+    _type = "lvm snapshot"
+
+    def __init__(self, name, parents=None, size=None, uuid=None,
+                 stripes=1, logSize=0, snapshotSpace=0,
+                 format=None, exists=False, sysfsPath='',
+                 grow=None, maxsize=None, percent=None, origin=None):
+        """ 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)
+
+                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 = kwargs.pop("origin", None)
+        if not self.origin:
+            raise DeviceError("lvm snapshot devices require an origin lv")
+
+        # XXX I think snapshotSpace is going to go away
+        # XXX what about stripes, logSize?
+        # XXX percent of what? vg or origin?
+        # XXX TODO: use this class when populating the devicetree
+        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"
@@ -2938,6 +2999,23 @@ 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)
+        lvm.thinsnapshotcreate(self.vg.name, self._name, self.origin.lvname)
+
 class MDRaidArrayDevice(StorageDevice):
     """ An mdraid (Linux RAID) device. """
     _type = "mdarray"
@@ -4613,8 +4691,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 _destroy(self):
         log_method_call(self, self.name, status=self.status)
@@ -4630,3 +4710,27 @@ 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")
+        self.read_only = kwargs.pop("read_only", False)
+        super(BTRFSSnapShotDevice, self).__init__(*args, **kwargs)
+        # TODO: detect existing snapshots?
+
+    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()
diff --git a/blivet/devicetree.py b/blivet/devicetree.py
index 763b4b6..a686ad8 100644
--- a/blivet/devicetree.py
+++ b/blivet/devicetree.py
@@ -1227,6 +1227,7 @@ class DeviceTree(object):
             lv_class = LVMLogicalVolumeDevice
             lv_name = lv_names[index]
             lv_parents = [vg_device]
+            lv_kwargs = {}
             name = "%s-%s" % (vg_name, lv_name)
             if lv_attr[index][0] in 'Ss':
                 log.info("found lvm snapshot volume '%s'" % name)
