diff --git a/blivet/__init__.py b/blivet/__init__.py
index 8ada1e0..230b965 100644
--- a/blivet/__init__.py
+++ b/blivet/__init__.py
@@ -1873,6 +1873,7 @@ class Blivet(object):
                 else:
                     root.mounts[mountpoint] = new_dev
 
+        log.debug("pre: %s ; post: %s", self.devicetree.eventManager, new.devicetree.eventManager)
         log.debug("finished Blivet copy")
         return new
 
diff --git a/blivet/actionlist.py b/blivet/actionlist.py
index 48cad7c..53014fc 100644
--- a/blivet/actionlist.py
+++ b/blivet/actionlist.py
@@ -42,6 +42,9 @@ class ActionList(object):
     def __iter__(self):
         return iter(self._actions)
 
+    def __list__(self):
+        return list(self._actions)
+
     def append(self, action):
         self._actions.append(action)
 
@@ -276,6 +279,7 @@ class ActionList(object):
         self._preProcess(devices=devices)
 
         for action in self._actions[:]:
+            print action
             log.info("executing action: %s", action)
             if dryRun:
                 continue
@@ -301,6 +305,7 @@ class ActionList(object):
                         device.format.device = device.path
 
                 self._completed_actions.append(self._actions.pop(0))
+                log.debug("done with action: %s", action)
 
             # give up some cycles to handle events
             time.sleep(0.2)
diff --git a/blivet/deviceaction.py b/blivet/deviceaction.py
index f734308..01c7994 100644
--- a/blivet/deviceaction.py
+++ b/blivet/deviceaction.py
@@ -303,6 +303,7 @@ class ActionCreateDevice(DeviceAction):
     def execute(self, callbacks=None):
         super(ActionCreateDevice, self).execute(callbacks=None)
         self.device.create()
+        self.device.setup()
 
     def requires(self, action):
         """ Return True if self requires action.
diff --git a/blivet/devices/container.py b/blivet/devices/container.py
index 569936f..5963c32 100644
--- a/blivet/devices/container.py
+++ b/blivet/devices/container.py
@@ -203,22 +203,8 @@ class ContainerDevice(StorageDevice):
             # they have no format.create method.
             parent.format.exists = True
 
-            ## Benefits to doing the below here:
-            ##  1. less type-specific code in the change handler
-            ##  2. container gets uuid set sooner
-            ##
-            ## Drawbacks to doing the below here:
-            ##  1. ...
-            ##
-
-            # Make sure that member and container UUIDs are synced up.
-            uuid = getattr(parent.format, self._formatUUIDAttr)
-            if uuid and not self.uuid:
-                # XXX This is also happening in DeviceTree._deviceChangedCB.
-                self.uuid = uuid
-            #elif self.uuid and not uuid:
-            #    # XXX I don't think this will ever happen.
-            #    setattr(parent.format, self._formatUUIDAttr, self.uuid)
+        # wait a couple seconds to get a UUID set by the event handler
+        self.modifySync.wait(2)
 
     # Container device operations manifest as events on member devices. We use
     # the parents' controlSync since it monitors events on the device itself
diff --git a/blivet/devices/luks.py b/blivet/devices/luks.py
index 197926f..a25cf6a 100644
--- a/blivet/devices/luks.py
+++ b/blivet/devices/luks.py
@@ -21,6 +21,7 @@
 
 # device backend modules
 from ..devicelibs import crypto
+from ..storage_log import log_method_call
 
 import logging
 log = logging.getLogger("blivet")
@@ -67,7 +68,12 @@ class LUKSDevice(DMCryptDevice):
             size = self.currentSize
         return size
 
+    def _setup(self, orig=False):
+        # this makes the post-setup sync happen
+        super(LUKSDevice, self)._setup(orig=orig)
+
     def _postCreate(self):
+        log_method_call(self, mapName=self.slave.format.mapName)
         self.name = self.slave.format.mapName
         StorageDevice._postCreate(self)
 
diff --git a/blivet/devices/storage.py b/blivet/devices/storage.py
index 1240fdb..c2e2cc6 100644
--- a/blivet/devices/storage.py
+++ b/blivet/devices/storage.py
@@ -404,6 +404,7 @@ class StorageDevice(Device):
 
     def _postSetup(self):
         """ Perform post-setup operations. """
+        log_method_call(self, status=self.status)
         event_sync = self.controlSync
         if self.__class__._setup != StorageDevice._setup:
             event_sync.wait()
diff --git a/blivet/devicetree.py b/blivet/devicetree.py
index 88112a9..0a8a889 100644
--- a/blivet/devicetree.py
+++ b/blivet/devicetree.py
@@ -22,6 +22,7 @@
 
 import os
 import re
+from itertools import chain
 
 from six import add_metaclass
 
@@ -165,7 +166,6 @@ class DeviceTree(object):
         return self.actions.find(*args, **kwargs)
 
     def processActions(self, callbacks=None, dryRun=False):
-        self.eventManager.enable()
         self.actions.process(callbacks, devices=self.devices, dryRun=dryRun)
 
     def _addDevice(self, newdev, new=True):
@@ -365,6 +365,47 @@ class DeviceTree(object):
 
         return dependents
 
+    def getDiskActions(self, disks):
+        """ Return a list of actions related to the specified disk.
+
+            :keyword disk: list of disks
+            :type disk: list of :class:`~.devices.StorageDevices`
+            :returns: list of related actions
+            :rtype: list of :class:`~.deviceaction.DeviceAction`
+
+            This includes all actions on the specified disks, plus all actions
+            on disks that are in any way connected to the specified disk via
+            container devices.
+        """
+        related_disks = set()
+        for action in self.actions:
+            if any(action.device.dependsOn(d) for d in disks):
+                related_disks.update(action.device.disks)
+
+        # now related_disks could be a superset of disks, so go through and
+        # build a list of actions related to any disk in related_disks
+        # Note that this list preserves the ordering of the action list.
+        related_actions = [a for a in self.actions
+                            if set(a.device.disks).intersection(related_disks)]
+        return related_actions
+
+    def cancelDiskActions(self, disks):
+        """ Cancel all actions related to the specified disk.
+
+            :keyword disk: list of disks
+            :type disk: list of :class:`~.devices.StorageDevices`
+
+            This includes actions related directly and indirectly (via container
+            membership, for example).
+        """
+        actions = self.getDiskActions(disks)
+        for action in reversed(actions):
+            self.cancelAction(action)
+
+    def cancelAllActions(self):
+        for action in reversed(list(self.actions)):
+            self.cancelAction(action)
+
     def hide(self, device):
         """ Hide the specified device.
 
@@ -408,17 +449,7 @@ class DeviceTree(object):
         if device.isDisk:
             # Cancel all actions on this disk and any disk related by way of an
             # aggregate/container device (eg: lvm volume group).
-            disks = [device]
-            related_actions = [a for a in self.actions
-                                    if a.device.dependsOn(device)]
-            for related_device in (a.device for a in related_actions):
-                disks.extend(related_device.disks)
-
-            disks = set(disks)
-            cancel = [a for a in self.actions
-                            if set(a.device.disks).intersection(disks)]
-            for action in reversed(cancel):
-                self.cancelAction(action)
+            self.cancelDiskActions(device)
 
         for d in self.getChildren(device):
             self.hide(d)
@@ -470,7 +501,6 @@ class DeviceTree(object):
         udev.settle()
 
         self.dropLVMCache()
-
         try:
             self.discoverer.populate(cleanupOnly=cleanupOnly)
         except Exception:
@@ -481,6 +511,8 @@ class DeviceTree(object):
         if flags.installer_mode:
             self.teardownAll()
 
+        self.eventManager.enable()
+
     def _isIgnoredDisk(self, disk):
         return ((self.ignoredDisks and disk.name in self.ignoredDisks) or
                 (self.exclusiveDisks and
@@ -986,31 +1018,25 @@ class DeviceTree(object):
                 device.format._mountpoint = mountpoint  # active mountpoint
                 device.format.mountopts = options
 
+    def __deepcopy__(self, memo):
+        return util.variable_copy(self, memo,
+                                  omit=('eventManager', 'eventHandler', 'discoverer'))
+
     def __str__(self):
         done = []
-        def get_depth(device):
-            depth = 0
-            _device = device
-            while True:
-                if _device.parents:
-                    depth += 1
-                    _device = _device.parents[0]
-                else:
-                    break
-
-            return depth
-
-        def show_subtree(root):
-            if root in done:
-                return ""
-            s = "%s%s\n" % ("  " * get_depth(root), root)
+        def show_subtree(root, depth):
+            abbreviate_subtree = root in done
+            s = "%s%s\n" % ("  " * depth, root)
             done.append(root)
-            for child in self.getChildren(root):
-                s+= show_subtree(child)
+            if abbreviate_subtree:
+                s += "%s...\n" % ("  " * (depth+1),)
+            else:
+                for child in self.getChildren(root):
+                    s += show_subtree(child, depth + 1)
             return s
 
         roots = [d for d in self._devices if not d.parents]
         tree = ""
         for root in roots:
-            tree += show_subtree(root)
+            tree += show_subtree(root, 0)
         return tree
diff --git a/blivet/discoverer.py b/blivet/discoverer.py
index e10a8e2..801d7b0 100644
--- a/blivet/discoverer.py
+++ b/blivet/discoverer.py
@@ -26,6 +26,7 @@ import os
 import pprint
 import re
 import shutil
+from itertools import chain
 
 from .errors import CryptoError, DeviceError, DeviceTreeError, FSError, InvalidDiskLabelError, LUKSError, MDRaidError, UnusableConfigurationError
 from .devices import BTRFSSubVolumeDevice, BTRFSVolumeDevice, BTRFSSnapShotDevice
@@ -198,9 +199,25 @@ class DeviceDiscoverer(object):
                      (udev.device_is_md(info) and
                       not udev.device_get_md_container(info))))
 
+    def _getSlaveNames(self, info):
+        """ Return a list of slave device names as found in sysfs.
+
+            :param :class:`pyudev.Device` info: the device's udev info
+            :returns: a list of slave/parent device names
+            :rtype: list of str
+        """
+        name = udev.device_get_name(info)
+        sysfs_path = udev.device_get_sysfs_path(info)
+        slave_dir = os.path.normpath("%s/slaves" % sysfs_path)
+        slave_names = []
+        if os.path.isdir(os.path.realpath(slave_dir)):
+            slave_names = os.listdir(slave_dir)
+        return slave_names
+        
     def _addSlaveDevices(self, info):
         """ Add all slaves of a device, raising DeviceTreeError on failure.
 
+            :param :class:`pyudev.Device` info: the device's udev info
             :raises: :class:`~.errors.DeviceTreeError if no slaves are found or
                      if we fail to add any slave
             :returns: a list of slave devices
@@ -208,23 +225,22 @@ class DeviceDiscoverer(object):
         """
         name = udev.device_get_name(info)
         sysfs_path = udev.device_get_sysfs_path(info)
-        slave_dir = os.path.normpath("%s/slaves" % sysfs_path)
-        slave_names = os.listdir(slave_dir)
+        slave_names = self._getSlaveNames(info)
         slave_devices = []
         if not slave_names:
             log.error("no slaves found for %s", name)
             raise DeviceTreeError("no slaves found for device %s" % name)
 
+        slave_dir = os.path.normpath("%s/slaves" % sysfs_path)
         for slave_name in slave_names:
             path = os.path.normpath("%s/%s" % (slave_dir, slave_name))
             slave_info = udev.get_device(os.path.realpath(path))
 
-            # cciss in sysfs is "cciss!cXdYpZ" but we need "cciss/cXdYpZ"
-            slave_name = udev.device_get_name(slave_info).replace("!", "/")
-
             if not slave_info:
                 log.warning("unable to get udev info for %s", slave_name)
 
+            # cciss in sysfs is "cciss!cXdYpZ" but we need "cciss/cXdYpZ"
+            slave_name = udev.device_get_name(slave_info).replace("!", "/")
             slave_dev = self.devicetree.getDeviceByName(slave_name)
             if not slave_dev and slave_info:
                 # we haven't scanned the slave yet, so do it now
@@ -247,6 +263,51 @@ class DeviceDiscoverer(object):
 
         return slave_devices
 
+    def getUdevDeviceDisks(self, info):
+        """ Return a list of disk names used by a udev device.
+
+            :param :class:`pyudev.Device` info: the device's udev info
+            :returns: a list of the disks used by the device
+            :rtype: list of str
+
+            .. note::
+
+                cciss names are returned in blivet-compatible form, eg:
+                cciss/c0d0p1 (not cciss!c0d0p1)
+
+        """
+        name = udev.device_get_name(info)
+        sysfs_path = udev.device_get_sysfs_path(info)
+        slave_dir = os.path.normpath("%s/slaves" % sysfs_path)
+        slave_names = self._getSlaveNames(info)
+        disks = []
+
+        # cciss in sysfs is "cciss!cXdYpZ" but we need "cciss/cXdYpZ"
+        name = name.replace("!", "/")
+
+        if self.udevDeviceIsDisk(info):
+            # this should catch multipath, dmraid, &c
+            disks.append(name)
+        elif udev.device_is_partition(info):
+            disk_name = udev.device_get_partition_disk(info)
+            if disk_name:
+                disks.append(disk_name)
+        elif not slave_names:
+            # loop, sr
+            disks.append(name)
+
+        for slave_name in slave_names:
+            path = os.path.normpath("%s/%s" % (slave_dir, slave_name))
+            slave_info = udev.get_device(os.path.realpath(path))
+            if not slave_info:
+                log.warning("unable to get udev info for %s", slave_name)
+                continue
+
+            slave_disks = self.getUdevDeviceDisks(slave_info)
+            disks.extend(d for d in slave_disks if d not in disks)
+
+        return disks
+
     def addUdevLVDevice(self, info):
         name = udev.device_get_name(info)
         log_method_call(self, name=name)
@@ -256,6 +317,8 @@ class DeviceDiscoverer(object):
         if device and not isinstance(device, LVMVolumeGroupDevice):
             log.warning("found non-vg device with name %s", vg_name)
             device = None
+        elif device and not device.exists:
+            raise InconsistentStateError()
 
         self._addSlaveDevices(info)
 
@@ -654,12 +717,20 @@ class DeviceDiscoverer(object):
 
                 device.format = None
 
+        if device is None:
+            disk_names = self.getUdevDeviceDisks(info)
+            disks = [d for d in
+                        [self.devicetree.getDeviceByName(n) for n in disk_names]
+                            if d is not None]
+            self.devicetree.cancelDiskActions(disks)
+
         #
         # The first step is to either look up or create the device
         #
+        added_device = True
         if device:
             # we successfully looked up the device. skip to format handling.
-            pass
+            added_device = False
         elif udev.device_is_loop(info):
             log.info("%s is a loop device", name)
             device = self.addUdevLoopDevice(info)
@@ -724,7 +795,9 @@ class DeviceDiscoverer(object):
 
         # now handle the device's formatting
         self.handleUdevDeviceFormat(info, device)
-        device.originalFormat = copy.copy(device.format)
+        if added_device:
+            log.debug("saving originalFormat")
+            device.originalFormat = copy.copy(device.format)
         device.deviceLinks = udev.device_get_symlinks(info)
 
     def handleUdevDiskLabelFormat(self, info, device):
@@ -834,7 +907,7 @@ class DeviceDiscoverer(object):
         for lv_device in vg_device.lvs[:]:
             if lv_device.name not in lv_info:
                 log.info("lv %s seems to have been removed", lv_device.name)
-                self.devicetree.recursiveRemove(lv_device, actions=False)
+                self.devicetree.cancelDiskActions(vg_device.disks)
 
         if not vg_device.complete:
             log.warning("Skipping LVs for incomplete VG %s", vg_name)
@@ -868,6 +941,8 @@ class DeviceDiscoverer(object):
                 if vol is None:
                     log.error("%s: %s", msg, name)
                     raise DeviceTreeError(msg)
+                elif not vol.exists:
+                    raise InconsistentStateError()
 
         def addLV(lv):
             """ Instantiate and add an LV based on data from the VG. """
@@ -884,13 +959,15 @@ class DeviceDiscoverer(object):
 
             lv_device = self.devicetree.getDeviceByName(name)
             if lv_device:
+                if not lv_device.exists:
+                    raise InconsistentStateError()
+
                 # some lvs may have been added on demand below
                 log.debug("already added %s", name)
                 if lv_size != lv_device.currentSize:
                     # lvresize can operate on an inactive lv, in which case
                     # the only notification we will receive is a change uevent
                     # for the pv(s)
-                    # FIXME: account for scheduled actions
                     lv_device.updateSize(newsize=lv_size)
 
                 return
@@ -911,6 +988,8 @@ class DeviceDiscoverer(object):
                     addRequiredLV(origin_device_name,
                                   "failed to locate origin lv")
                     origin = self.devicetree.getDeviceByName(origin_device_name)
+                    if origin and not origin.exists:
+                        raise InconsistentStateError()
 
                 lv_kwargs["origin"] = origin
                 lv_class = LVMSnapShotDevice
@@ -956,12 +1035,17 @@ class DeviceDiscoverer(object):
                     origin_device_name = "%s-%s" % (vg_name, origin_name)
                     addRequiredLV(origin_device_name, "failed to locate origin lv")
                     origin = self.devicetree.getDeviceByName(origin_device_name)
+                    if origin and not origin.exists:
+                        raise InconsistentStateError()
+
                     lv_kwargs["origin"] = origin
                     lv_class = LVMThinSnapShotDevice
                 else:
                     lv_class = LVMThinLogicalVolumeDevice
 
                 lv_parents = [self.devicetree.getDeviceByName(pool_device_name)]
+                if lv_parents and not lv_parents[0].exists:
+                    raise InconsistentStateError()
             elif lv_name.endswith(']'):
                 # Internal LVM2 device
                 return
@@ -978,6 +1062,7 @@ class DeviceDiscoverer(object):
 
             lv_dev = self.devicetree.getDeviceByUuid(lv_uuid)
             if lv_dev is None:
+                self.devicetree.cancelDiskActions(vg_device.disks)
                 lv_device = lv_class(lv_name, parents=lv_parents,
                                      uuid=lv_uuid, size=lv_size,segType=lv_type,
                                      exists=True, **lv_kwargs)
@@ -994,6 +1079,8 @@ class DeviceDiscoverer(object):
 
                     # do format handling now
                     self.addUdevDevice(lv_info)
+            elif not lv_dev.exists:
+                raise InconsistentStateError()
 
         raid = dict((n.replace("[", "").replace("]", ""),
                      {"copies": 0, "log": Size(0), "meta": Size(0)})
@@ -1006,6 +1093,8 @@ class DeviceDiscoverer(object):
             if not lv_dev:
                 # hidden lv, eg: pool00_tdata
                 continue
+            elif not lv_dev.exists:
+                raise InconsistentStateError()
 
             lv_dev.copies = data["copies"] or 1
             lv_dev.metaDataSize = data["meta"]
@@ -1273,6 +1362,8 @@ class DeviceDiscoverer(object):
             log.debug("no type or existing type for %s, bailing", name)
             return
 
+        self.devicetree.cancelDiskActions(device.disks)
+
         # set up the common arguments for the format constructor
         format_designator = format_type
         kwargs = {"uuid": uuid,
diff --git a/blivet/errors.py b/blivet/errors.py
index c3c2507..144ba3b 100644
--- a/blivet/errors.py
+++ b/blivet/errors.py
@@ -155,6 +155,10 @@ class UnusableConfigurationError(StorageError):
     """ User has an unusable initial storage configuration. """
     pass
 
+class InconsistentStateError(StorageError):
+    """ Outside changes conflict with scheduled actions. """
+    pass
+
 # DeviceAction
 class DeviceActionError(StorageError):
     pass
diff --git a/blivet/event.py b/blivet/event.py
index ebcd24c..8c715d4 100644
--- a/blivet/event.py
+++ b/blivet/event.py
@@ -23,7 +23,7 @@
 import abc
 import copy
 from collections import deque
-from threading import RLock, Timer
+from threading import RLock, Timer, currentThread
 import pyudev
 from six import add_metaclass
 
@@ -123,6 +123,9 @@ class EventQueue(object):
 
             return self._queue.popleft()
 
+    def blacklist_clear(self):
+        self._blacklist = []
+
     def blacklist_add(self, device=None, action=None, count=1):
         """ Ignore future events.
 
@@ -158,7 +161,7 @@ class EventQueue(object):
 ## EventManager
 ##
 @add_metaclass(abc.ABCMeta)
-class EventManager(object):
+class EventManager(util.ObjectID):
     _event_queue_class = EventQueue
 
     def __init__(self, handler_cb=None, notify_cb=None):
@@ -230,6 +233,9 @@ class EventManager(object):
         """
         self._queue.blacklist_add(device=device, action=action, count=count)
 
+    def blacklist_clear(self):
+        self._queue.blacklist_clear()
+
     def next_event(self):
         return self._queue.dequeue()
 
@@ -253,8 +259,8 @@ class UdevEventManager(EventManager):
                                                notify_cb=notify_cb)
         self._pyudev_observer = None
 
-    def __deepcopy__(self, memo):
-        return util.variable_copy(self, memo, shallow=('_pyudev_observer'))
+    def __str__(self):
+        return "%s [%d]" % (self.__class__.__name__, self.id)
 
     @property
     def enabled(self):
@@ -280,9 +286,13 @@ class UdevEventManager(EventManager):
 
     def enqueue_event(self, *args, **kwargs):
         event = UdevEvent(args[0], args[1])
+        print "->", event
         log.debug("-> %s", event)
         self._queue.enqueue(event)
 
     def handle_event(self, *args, **kwargs):
         """ Enqueue a uevent and call the configured handler. """
+        current_thread = currentThread()
+        current_thread.name = current_thread.name.replace("Thread-",
+                                                          "EventManager")
         super(UdevEventManager, self).handle_event(args[0], args[1])
diff --git a/blivet/formats/__init__.py b/blivet/formats/__init__.py
index 1e49022..83a715f 100644
--- a/blivet/formats/__init__.py
+++ b/blivet/formats/__init__.py
@@ -309,9 +309,10 @@ class DeviceFormat(ObjectID):
     )
 
     def updateSyncPassthrough(self):
-        self.eventSync.passthrough = (self.device and
+        passthrough = (self.device and
                                (not os.path.exists(self.device) or
                                 not stat.S_ISBLK(os.stat(self.device).st_mode)))
+        self.eventSync.passthrough = passthrough
 
     def _setDevice(self, devspec):
         if devspec and not devspec.startswith("/"):
@@ -402,6 +403,7 @@ class DeviceFormat(ObjectID):
             synchronizer themselves.
         """
         if self.createGeneratesEvent:
+            self.updateSyncPassthrough()
             self._setCreateEventInfo()
             self.eventSync.creating = True
 
@@ -456,6 +458,7 @@ class DeviceFormat(ObjectID):
     def _destroy(self, **kwargs):
         notify = kwargs.pop("notify", True)
         if notify and self.destroyGeneratesEvent:
+            self.updateSyncPassthrough()
             self._setDestroyEventInfo()
             self.eventSync.destroying = True
 
@@ -478,6 +481,7 @@ class DeviceFormat(ObjectID):
         if notify and self.destroyGeneratesEvent:
             self.eventSync.wait()
         self.exists = False
+        self.eventSync.reset()
         if notify and self.destroyGeneratesEvent:
             self.eventSync.notify()
 
diff --git a/blivet/formats/disklabel.py b/blivet/formats/disklabel.py
index 93438fc..ca0a466 100644
--- a/blivet/formats/disklabel.py
+++ b/blivet/formats/disklabel.py
@@ -121,6 +121,8 @@ class DiskLabel(DeviceFormat):
 
     def updatePartedDisk(self):
         """ re-read the disklabel from the device """
+        #self._partedDevice.removeFromCache()
+        #self._partedDevice = None
         self._partedDisk = None
         self.updateOrigPartedDisk()
 
diff --git a/blivet/formats/luks.py b/blivet/formats/luks.py
index d7a9a92..51bab26 100644
--- a/blivet/formats/luks.py
+++ b/blivet/formats/luks.py
@@ -34,6 +34,7 @@ from ..errors import LUKSError
 from ..devicelibs import crypto
 from . import DeviceFormat, register_device_format
 from ..i18n import _, N_
+from ..flags import flags
 
 import logging
 log = logging.getLogger("blivet")
@@ -161,15 +162,17 @@ class LUKS(DeviceFormat):
 
     @property
     def status(self):
+        log_method_call(self, exists=self.exists, mapName=self.mapName)
         if not self.exists or not self.mapName:
             return False
         return os.path.exists("/dev/mapper/%s" % self.mapName)
 
     def _preSetup(self, **kwargs):
-        super(LUKS, self)._preSetup(**kwargs)
         if not self.configured:
             raise LUKSError("luks device not configured")
 
+        return super(LUKS, self)._preSetup(**kwargs)
+
     def _setup(self, **kwargs):
         """ Open the encrypted block device.
 
@@ -229,6 +232,7 @@ class LUKS(DeviceFormat):
         """
         log_method_call(self, device=self.device,
                         type=self.type, status=self.status)
+        super(LUKS, self)._create(**kwargs)
         crypto.luks_format(self.device,
                          passphrase=self.__passphrase,
                          key_file=self._key_file,
@@ -238,7 +242,11 @@ class LUKS(DeviceFormat):
 
     def _postCreate(self, **kwargs):
         super(LUKS, self)._postCreate(**kwargs)
-        self.mapName = "luks-%s" % self.uuid
+        log_method_call(self, uuid=self.uuid)
+        if flags.installer_mode or not self.mapName:
+            self.eventSync.wait(timeout=2) # wait til the uuid is set
+            log.debug("updated UUID is %s", self.uuid) 
+            self.mapName = "luks-%s" % self.uuid
 
     def _destroy(self, **kwargs):
         self.teardown()
diff --git a/blivet/formats/lvmpv.py b/blivet/formats/lvmpv.py
index 3348ec2..d8d4fb2 100644
--- a/blivet/formats/lvmpv.py
+++ b/blivet/formats/lvmpv.py
@@ -119,6 +119,10 @@ class LVMPhysicalVolume(DeviceFormat):
     def _destroy(self, **kwargs):
         log_method_call(self, device=self.device,
                         type=self.type, status=self.status)
+        self.updateSyncPassthrough()
+        self._setDestroyEventInfo()
+        self.eventSync.destroying = True
+
         try:
             lvm.pvremove(self.device)
         except LVMError:
diff --git a/blivet/handler.py b/blivet/handler.py
index 3a25a82..03a84f9 100644
--- a/blivet/handler.py
+++ b/blivet/handler.py
@@ -23,6 +23,7 @@
 import os
 import re
 import pprint
+from threading import currentThread
 from six import add_metaclass
 
 from . import udev
@@ -50,6 +51,8 @@ class EventHandler(object):
             log.debug("uevent queue is empty")
             return
 
+        currentThread().name = "Event%d" % event.id
+        print event
         log.debug("event: %s", event)
         if event.action == "add":
             self.deviceAddedCB(event.info)
@@ -82,6 +85,7 @@ class EventHandler(object):
         log_method_call(self, name=name, action=action, sysfs_path=sysfs_path)
 
         actions = self.devicetree.findActions()
+        log.debug("%d actions queued", len(actions))
 
         # We can't do this lookup by sysfs path since the StorageDevice
         # might have just been created, in which case it might not have a
@@ -164,6 +168,7 @@ class EventHandler(object):
                 else:
                     device = None
 
+                log.debug("found removed device %s", device)
                 if action == "change" and not device:
                     # see if this is a format op on a format scheduled for
                     # destruction
@@ -180,6 +185,7 @@ class EventHandler(object):
                             name = action.device.name
                             break
 
+                log.debug("event_sync is %s", event_sync)
                 # If we haven't come up with an event_sync yet, try the same
                 # checks on the device being operated on by the current action,
                 # if any.
@@ -187,6 +193,7 @@ class EventHandler(object):
                 #     action is not being executed.
                 if device and not event_sync and actions:
                     current_action = actions[0]
+                    log.debug("current action is %s", current_action)
                     if current_action.device.controlSync.starting and \
                        not current_action.device.delegateControlEvents:
                         ## setup of device scheduled for destruction
@@ -243,6 +250,9 @@ class EventHandler(object):
     def deviceAddedCB(self, info, force=False):
         """ Handle an "add" uevent on a block device.
 
+            :param :class:`pyudev.Device` info: the device's udev info
+            :keyword bool force: whether to handle md/dm add events
+
             The device could be newly created or newly activated.
         """
         sysfs_path = udev.device_get_sysfs_path(info)
@@ -279,7 +289,10 @@ class EventHandler(object):
             return
 
         # If we get here this should be a device that was added from outside of
-        # blivet. Add it to the devicetree.
+        # blivet.
+        # Cancel all actions
+
+        # Add it to the devicetree.
         device = self.devicetree.discoverer.addUdevDevice(info)
         if device:
             # if this device is on a hidden disk it should also be hidden
@@ -289,7 +302,7 @@ class EventHandler(object):
         log.info("checking for changes to disklabel on %s", device.name)
         # update the partition list
         self.manager.blacklist_event(device=udev.device_get_name(info),
-                                          action="change", count=1)
+                                          action="change", count=2)
         device.format.updatePartedDisk()
         udev_devices = [d for d in udev.get_devices()
                 if udev.device_get_disklabel_uuid(d) == device.format.uuid and
@@ -299,10 +312,12 @@ class EventHandler(object):
             return int(start) if start is not None else start
 
         # remove any partitions we have that are no longer on disk
+        remove = []
         for old in self.devicetree.getChildren(device):
             if not old.exists:
                 log.warning("non-existent partition %s on changed "
                             "disklabel", old.name)
+                remove.append(old)
                 continue
 
             if old.isLogical:
@@ -327,16 +342,25 @@ class EventHandler(object):
 
             if new is None:
                 log.info("partition %s was removed", old.name)
-                self.devicetree.recursiveRemove(old, actions=False,
-                                                modparent=False)
+                remove.append(old)
             else:
                 udev_devices.remove(new)
 
+        if remove or udev_devices:
+            self.devicetree.cancelDiskActions([device])
+
+        for old in remove:
+            if old not in self.devicetree.devices:
+                # may have been removed by action cancelation
+                continue
+                
+            self.devicetree.recursiveRemove(old, actions=False, modparent=False)
+
         # any partitions left in the list have been added
         for new in udev_devices:
             log.info("partition %s was added",
                      udev.device_get_name(new))
-            self.devicetree.discoverer.discoverer.addUdevDevice(new)
+            self.devicetree.discoverer.addUdevDevice(new)
 
     def _getMemberUUID(self, info, device, container=False):
         uuid = udev.device_get_uuid(info)
@@ -376,11 +400,17 @@ class EventHandler(object):
 
         old_container_uuid = device.format.containerUUID
         container_changed = (old_container_uuid != container_uuid)
+        if container_changed:
+            self.devicetree.cancelDiskActions(device.disks)
+
         try:
             container = self.devicetree.getChildren(device)[0]
         except IndexError:
             container = None
 
+        if container and not container.exists:
+            self.devicetree.cancelDiskActions(device.disks)
+
         if container_changed:
             if container:
                 if len(container.parents) == 1:
@@ -407,15 +437,30 @@ class EventHandler(object):
                 device.format.vgName = new_vg_name
                 device.format.peStart = udev.device_get_pv_pe_start(pv_info)
                 if container:
-                    # vg rename
                     container.name = new_vg_name
-                    self.devicetree.updateLVs(container)
+                    try:
+                        self.devicetree.discoverer.updateLVs(container)
+                    except InconsistentStateError:
+                        self.devicetree.cancelDiskActions(container.disks)
+                        self.devicetree.discoverer.updateLVs(container)
 
         # MD TODO: raid level, spares
         # BTRFS TODO: check for changes to subvol list IFF the volume is mounted
 
     def deviceChangedCB(self, info, expected=False):
-        """ Handle a "changed" uevent on a block device. """
+        """ Handle a "changed" uevent on a block device.
+
+            :param :class:`pyudev.Device` info: the device's udev info
+            :keyword bool expected: whether this event was expected
+
+            Expected events are those believed to be the product of some
+            operation carried out by blivet.
+
+            TODO: We should try to handle events on hidden disks, but we need
+                  not be concerned with canceling actions on hidden disks since
+                  actions related to hidden disks are always canceled as part
+                  of the hiding process.
+        """
         sysfs_path = udev.device_get_sysfs_path(info)
         log.debug("device changed: %s", sysfs_path)
         if info.subsystem != "block":
@@ -431,6 +476,9 @@ class EventHandler(object):
         # It will get set/updated in _notifyDevice or below that call in this
         # method.
         name = udev.device_get_name(info)
+        if name.startswith("temporary-cryptsetup-"):
+            return
+
         device = self.devicetree.getDeviceByName(name, hidden=True)
         if device and device.exists:
             device.sysfsPath = sysfs_path
@@ -449,15 +497,13 @@ class EventHandler(object):
         if not device:
             # We're not concerned with updating devices that have been removed
             # from the tree.
-            log.warning("device not found: %s", udev.device_get_name(info))
+            log.info("device not found: %s", udev.device_get_name(info))
             return
 
         if not device.exists:
-            #
-            # A policy must be decided upon for external events that conflict
-            # with scheduled actions.
-            #
-            log.warning("ignoring change uevent on non-existent device")
+            # FIXME: we need to cancel some actions or refine this block's test
+            if not expected:
+                log.error("ignoring change uevent on non-existent device %s", device.name)
             return
 
         if not os.path.exists(device.path):
@@ -471,6 +517,8 @@ class EventHandler(object):
         # rename
         name = info.get("DM_LV_NAME", udev.device_get_name(info))
         if getattr(device, "lvname", "name") != name:
+            # UPDATE ACTION DEVICES
+            # UPDATE FORMAT DEVICE PATHS
             device.name = name
 
         # resize
@@ -478,6 +526,8 @@ class EventHandler(object):
         #     handler for pv(s))
         current_size = device.readCurrentSize()
         if expected or device.currentSize != current_size:
+            if not expected:
+                self.devicetree.cancelDiskActions(device.disks)
             device.updateSize(newsize=current_size)
 
         # This is also happening in ContainerDevice._postCreate.
@@ -489,7 +539,8 @@ class EventHandler(object):
             # A policy must be decided upon for external events that conflict
             # with scheduled actions.
             #
-            log.warning("ignoring change uevent on device with non-existent format")
+            if not expected:
+                log.warning("ignoring change uevent on device with non-existent format")
             return
 
         log.debug("changed: %s", pprint.pformat(dict(info)))
@@ -501,7 +552,8 @@ class EventHandler(object):
         label = udev.device_get_label(info)
 
         partitioned = (device.partitionable and
-                       info.get("ID_PART_TABLE_TYPE") is not None)
+                       info.get("ID_PART_TABLE_TYPE") is not None and
+                       "ID_PART_ENTRY_DISK" not in info)
         new_type = getFormat(udev.device_get_format(info)).type
         type_changed = (new_type != device.format.type and
                         not
@@ -516,41 +568,48 @@ class EventHandler(object):
         uuid_changed = (device.format.uuid and device.format.uuid != uuid)
         reformatted = uuid_changed or type_changed
 
-        if not type_changed and (expected or not uuid_changed):
-            log.info("%s was not reformatted, or was reformatted by blivet",
-                     device.name)
-            ##
-            ## Not reformatted or reformatted as expected by blivet.
-            ##
+        if not type_changed:
+            if hasattr(device.format, "label"):
+                device.format.label = label
 
-            ## update UUID and label
-            device.format.uuid = uuid
+            if expected:
+                device.format.uuid = uuid
 
-            # FIXME: grab the info about uuid attrs from the container instance?
-            if expected and hasattr(device.format, "containerUUID"):
-                if device.format.type == "lvmpv":
-                    self.devicetree.dropLVMCache()
-                device.format.containerUUID = self._getContainerUUID(info,
-                                                                     device)
+                if hasattr(device.format, "containerUUID"):
+                    if device.format.type == "lvmpv":
+                        self.devicetree.dropLVMCache()
+                    device.format.containerUUID = self._getContainerUUID(info,
+                                                                         device)
+                    try:
+                        container = self.devicetree.getChildren(device)[0]
+                    except IndexError:
+                        pass
+                    else:
+                        # notify the container in case it's waiting on the UUID
+                        # in _postCreate
+                        container.modifySync.notify()
+
+                # notify the event sync now that we're done updating UUID in
+                # case it is waiting on us to set it
+                device.format.eventSync.notify()
 
-            if hasattr(device.format, "label"):
-                device.format.label = label
-        elif reformatted and not expected:
+        if expected:
+            return
+
+        if not expected:
+            log.error("ignoring unexpected change event on %s", name)
+            return
+
+        if reformatted:
             log.info("%s was reformatted from outside of blivet", device.name)
+            self.devicetree.cancelDiskActions(device.disks)
+            # eg: wipefs on lvm pv w/ configured vg and lvs
             for child in self.devicetree.getChildren(device):
                 self.devicetree.recursiveRemove(child, actions=False)
 
             device.format = None
             self.devicetree.handleUdevDeviceFormat(info, device)
-
-        if expected:
-            return
-
-        ##
-        ## Now handle devices whose formatting determines other devices'
-        ## existence.
-        ##
-        if partitioned:
+        elif partitioned:
             self._diskLabelChangeHandler(info, device)
         elif hasattr(device.format, "containerUUID"):
             if device.format.type == "lvmpv":
@@ -587,4 +646,3 @@ class EventHandler(object):
                     fmt._mountpoint = None
                     fmt._mounted_read_only = False
 
-
diff --git a/blivet/util.py b/blivet/util.py
index 77fe773..d76416a 100644
--- a/blivet/util.py
+++ b/blivet/util.py
@@ -450,7 +450,7 @@ def set_up_logging(log_file='/tmp/blivet.log'):
     program_log.setLevel(logging.DEBUG)
     handler = logging.FileHandler(log_file)
     handler.setLevel(logging.DEBUG)
-    formatter = logging.Formatter("%(asctime)s %(levelname)s %(name)s: %(message)s")
+    formatter = logging.Formatter("%(asctime)s %(levelname)s %(name)s/%(threadName)s: %(message)s")
     handler.setFormatter(formatter)
     log.addHandler(handler)
     program_log.addHandler(handler)
diff --git a/examples/common.py b/examples/common.py
index 48cb67d..c20bb33 100644
--- a/examples/common.py
+++ b/examples/common.py
@@ -1,5 +1,7 @@
 
 def print_devices(b):
+    print b.devicetree
+    return
     print
     for device in sorted(b.devices, key=lambda d: len(d.ancestors)):
         print device
diff --git a/examples/factory.py b/examples/factory.py
index 7c9cd83..2a4d55e 100644
--- a/examples/factory.py
+++ b/examples/factory.py
@@ -26,6 +26,7 @@ try:
     # create an lv named data in a vg named testvg
     device = b.factoryDevice(blivet.devicefactory.DEVICE_TYPE_LVM,
                              Size("50GiB"), disks=[disk1, disk2],
+                             container_encrypted=True,
                              fstype="xfs", mountpoint="/data")
     print_devices(b)
 
@@ -34,7 +35,13 @@ try:
                              Size("50GiB"), disks=[disk1, disk2],
                              fstype="xfs", mountpoint="/data",
                              container_raid_level="raid1",
+                             container_encrypted=True,
                              device=device)
+    for device in b.devices:
+        if not device.format.exists and device.format.type == "luks" and \
+           not getattr(device.format, "configured", True):
+            device.format.passphrase = "qweqweqwe"
+
     print_devices(b)
 
     b.devicetree.processActions()
diff --git a/examples/lvm.py b/examples/lvm.py
index 90208be..2e26b87 100644
--- a/examples/lvm.py
+++ b/examples/lvm.py
@@ -9,47 +9,28 @@ from blivet.util import set_up_logging, create_sparse_tempfile
 set_up_logging()
 b = blivet.Blivet()   # create an instance of Blivet (don't add system devices)
 
-# create a disk image file on which to create new devices
-disk1_file = create_sparse_tempfile("disk1", Size("100GiB"))
-b.config.diskImages["disk1"] = disk1_file
-
+b.config.exclusiveDisks = ["sdb"]
 b.reset()
 
 try:
-    disk1 = b.devicetree.getDeviceByName("disk1")
-
+    disk1 = b.devicetree.getDeviceByName("sdb")
+    b.devicetree.recursiveRemove(disk1)
     b.initializeDisk(disk1)
 
-    pv = b.newPartition(size=Size("50GiB"), fmt_type="lvmpv")
-    b.createDevice(pv)
-
-    # allocate the partitions (decide where and on which disks they'll reside)
-    blivet.partitioning.doPartitioning(b)
-
-    vg = b.newVG(parents=[pv])
-    b.createDevice(vg)
-
-    # new lv with base size 5GiB and unbounded growth and an ext4 filesystem
-    dev = b.newLV(fmt_type="ext4", size=Size("5GiB"), grow=True,
-                  parents=[vg], name="unbounded")
-    b.createDevice(dev)
-
-    # new lv with base size 5GiB and growth up to 15GiB and an ext4 filesystem
-    dev = b.newLV(fmt_type="ext4", size=Size("5GiB"), grow=True,
-                  maxsize=Size("15GiB"), parents=[vg], name="bounded")
-    b.createDevice(dev)
+    device = b.factoryDevice(blivet.devicefactory.DEVICE_TYPE_LVM,
+                             Size("500MiB"), encrypted=True, name="private",
+                             disks=b.disks, fstype="xfs")
+    device.slave.format.mapName = "private"
+    device.slave.format.passphrase = "qweqweqwe"
 
-    # new lv with a fixed size of 2GiB formatted as swap space
-    dev = b.newLV(fmt_type="swap", size=Size("2GiB"), parents=[vg])
-    b.createDevice(dev)
+    #device = b.factoryDevice(blivet.devicefactory.DEVICE_TYPE_LVM,
+    #                         Size("2 GiB"),
+    #                         name="public", disks=b.disks, fstype="ext4")
 
-    # allocate the growable lvs
-    blivet.partitioning.growLVM(b)
     print_devices(b)
 
     # write the new partitions to disk and format them as specified
     b.doIt()
     print_devices(b)
 finally:
-    b.devicetree.teardownDiskImages()
-    os.unlink(disk1_file)
+    pass
