diff --git a/blivet/__init__.py b/blivet/__init__.py
index 9420c50..4f9f630 100644
--- a/blivet/__init__.py
+++ b/blivet/__init__.py
@@ -60,6 +60,7 @@ except ImportError:
     nss = None
 
 import parted
+import pyudev
 
 from pykickstart.constants import *
 
@@ -77,6 +78,7 @@ from devicelibs.dm import name_from_dm_node
 from devicelibs.crypto import generateBackupPassphrase
 from devicelibs.edd import get_edd_dict
 from udev import udev_trigger
+from udev import global_udev
 import iscsi
 import fcoe
 import zfcp
@@ -163,6 +165,7 @@ def turnOnFilesystems(storage, mountOnly=False):
             # turn off any swaps that we didn't turn on
             # needed for live installs
             util.run_program(["swapoff", "-a"])
+
         storage.devicetree.teardownAll()
 
         try:
@@ -306,6 +309,26 @@ class Blivet(object):
         self.roots = []
         self.services = set()
 
+    def ueventCB(self, action, info):
+        if action == "add":
+            self.devicetree.deviceAddedCB(info)
+            # TODO: update self.roots if in installer_mode
+            print [d.name for d in sorted(self.devices, key=lambda d: len(d.ancestors))]
+        elif action == "remove":
+            self.devicetree.deviceRemovedCB(info)
+            print [d.name for d in sorted(self.devices, key=lambda d: len(d.ancestors))]
+            # TODO: update self.roots
+        elif action == "change":
+            self.devicetree.deviceChangedCB(info)
+        else:
+            log.info("unknown uevent action: %s (%s)" % (action, info.sys_name))
+
+    def enableUeventMonitoring(self):
+        monitor = pyudev.Monitor.from_netlink(global_udev)
+        monitor.filter_by("block")
+        observer = pyudev.MonitorObserver(monitor, self.ueventCB)
+        observer.start()
+
     def doIt(self):
         self.devicetree.processActions()
         self.doEncryptionPassphraseRetrofits()
diff --git a/blivet/devicetree.py b/blivet/devicetree.py
index c7e0e72..3cc32b6 100644
--- a/blivet/devicetree.py
+++ b/blivet/devicetree.py
@@ -250,6 +250,42 @@ class DeviceTree(object):
 
                 self._completed_actions.append(self._actions.pop(0))
 
+    def deviceAddedCB(self, info):
+        log.debug("device added: %s" % udev_device_get_sysfs_path(info))
+        new_info = udev_get_device(udev_device_get_sysfs_path(info))
+        if not new_info.is_initialized:
+            log.debug("new device not initialized -- not processing it")
+            return
+
+        self.addUdevDevice(new_info)
+        self._applyDiskFilters()
+
+    def deviceChangedCB(self, info):
+        log.debug("device changed: %s" % udev_device_get_sysfs_path(info))
+        new_info = udev_get_device(udev_device_get_sysfs_path(info))
+        device = self.getDeviceBySysfsPath(udev_device_get_sysfs_path(new_info))
+        fmt = getFormat(udev_device_get_format(new_info))
+        if device and ((device.format.type != fmt.type) or
+                       (device.partitionable and
+                        new_info.get("ID_PART_TABLE_TYPE"))):
+            self.handleUdevDeviceFormat(new_info, device)
+
+    def deviceRemovedCB(self, info):
+        log.debug("device removed: %s" % udev_device_get_sysfs_path(info))
+        for device in self._hidden:
+            if device.sysfsPath == udev_device_get_sysfs_path(info):
+                device.unhide()
+
+        remove = []
+        for device in self._devices:
+            if device.sysfsPath == udev_device_get_sysfs_path(info):
+                remove.append(device)
+
+        remove.sort(key=lambda d: len(d.ancestors), reverse=True)
+        for device in remove:
+            self.cancelDeviceActions(device)
+            self._removeDevice(device)
+
     def _addDevice(self, newdev):
         """ Add a device to the tree.
 
@@ -281,6 +317,12 @@ class DeviceTree(object):
         """ Remove a device from the tree.
 
             Only leaves may be removed.
+
+            What are the various circumstances in which we might remove a device
+            from the tree?
+                - scheduling a destroy action
+                - canceling a create action
+                - removing an existing device that has disappeared
         """
         if dev not in self._devices:
             raise ValueError("Device '%s' not in tree" % dev.name)
@@ -976,7 +1018,7 @@ class DeviceTree(object):
 
     def addUdevDevice(self, info):
         name = udev_device_get_name(info)
-        log_method_call(self, name=name, info=pprint.pformat(info))
+        log_method_call(self, name=name, info=pprint.pformat(dict(info)))
         uuid = udev_device_get_uuid(info)
         sysfs_path = udev_device_get_sysfs_path(info)
 
@@ -1014,6 +1056,23 @@ class DeviceTree(object):
                 # make sure any device we found is an md device
                 device = None
 
+        # if we found a device with the same name but our device does not exist
+        # we must remove the non-existent device and add the existing one
+        # XXX This whole thing is nuts.
+        if device and not device.exists:
+            for disk in device.disks:
+                self.cancelDeviceActions(disk)  # no splitting hairs
+
+            # just in case
+            related_devices = device.ancestors + device + \
+                                self.getDependentDevices(device)
+            for related in reversed(related_devices):
+                if related in self._devices:
+                    self._removeDevice(related)
+                    
+            self.addUdevDevice(info)
+            return
+
         #
         # The first step is to either look up or create the device
         #
@@ -1021,6 +1080,8 @@ class DeviceTree(object):
             # we successfully looked up the device. skip to format handling.
             # first, grab the parted.Device while it's active
             _unused = device.partedDevice
+
+            # check for changes to this device?
         elif udev_device_is_loop(info):
             log.info("%s is a loop device" % name)
             device = self.addUdevLoopDevice(info)
@@ -1083,7 +1144,6 @@ class DeviceTree(object):
         log.info("got device: %r" % device)
         if device.format.type:
             log.info("got format: %r" % device.format)
-        device.originalFormat = copy.copy(device.format)
         device.deviceLinks = udev_device_get_symlinks(info)
 
     def handleUdevDiskLabelFormat(self, info, device):
@@ -1693,6 +1753,8 @@ class DeviceTree(object):
         elif device.format.type == "btrfs":
             self.handleBTRFSFormat(info, device)
 
+        device.originalFormat = copy.copy(device.format)
+
     def updateDeviceFormat(self, device):
         log.info("updating format of device: %s" % device)
         try:
@@ -1717,17 +1779,7 @@ class DeviceTree(object):
             for pv in vg.pvs:
                 devicelibs.lvm.lvm_cc_addFilterRejectRegexp(pv.name)
 
-    def hide(self, device):
-        if device in self._hidden:
-            return
-
-        for d in self.getChildren(device):
-            self.hide(d)
-
-        log.info("hiding device %s %s (id %d)" % (device.type,
-                                                  device.name,
-                                                  device.id))
-
+    def cancelDeviceActions(self, device):
         for action in reversed(self._actions):
             if not action.device.dependsOn(device) and action.device != device:
                 continue
@@ -1741,6 +1793,19 @@ class DeviceTree(object):
             finally:
                 self._actions.remove(action)
 
+    def hide(self, device):
+        if device in self._hidden:
+            return
+
+        for d in self.getChildren(device):
+            self.hide(d)
+
+        log.info("hiding device %s %s (id %d)" % (device.type,
+                                                  device.name,
+                                                  device.id))
+
+        self.cancelDeviceActions(device)
+
         # XXX modifications that do not require actions, like setting a
         #     mountpoint, will not be reversed here
 
@@ -1915,12 +1980,18 @@ class DeviceTree(object):
         # inconsistencies are ignored or resolved.
         self._handleInconsistencies()
 
-        self.teardownAll()
+        self._applyDiskFilters()
 
+    def _applyDiskFilters(self):
         # hide any subtrees that begin with an ignored disk
         for disk in [d for d in self._devices if d.isDisk]:
             if ((self.ignoredDisks and disk.name in self.ignoredDisks) or
                 (self.exclusiveDisks and disk.name not in self.exclusiveDisks)):
+                deps = self.getDependentDevices(disk)
+                deps.sort(key=lambda d: len(d.ancestors), reverse=True)
+                for dep in deps:
+                    dep.teardown()
+
                 self.hide(disk)
 
     def teardownAll(self):
