diff --git a/pyanaconda/bootloader.py b/pyanaconda/bootloader.py
index 971c0dc..fac9ee8 100644
--- a/pyanaconda/bootloader.py
+++ b/pyanaconda/bootloader.py
@@ -620,6 +620,7 @@ class BootLoader(object):
         return valid
 
     def set_stage1_device(self, devices):
+        self.stage1_device = None
         if not self.stage1_disk:
             raise BootLoaderError("need stage1 disk to set stage1 device")
 
@@ -627,7 +628,6 @@ class BootLoader(object):
             self.stage1_device = self.stage2_device
             return
 
-        self.stage1_device = None
         for device in devices:
             if self.stage1_disk not in device.disks:
                 continue
diff --git a/pyanaconda/installclass.py b/pyanaconda/installclass.py
index 17a0c11..3f7bc67 100644
--- a/pyanaconda/installclass.py
+++ b/pyanaconda/installclass.py
@@ -104,7 +104,7 @@ class BaseInstallClass(object):
         # provided as a way for other products to specify their own.
         return None
 
-    def setDefaultPartitioning(self, storage, platform):
+    def setDefaultPartitioning(self, storage):
         autorequests = [PartSpec(mountpoint="/", fstype=storage.defaultFSType,
                                  size=1024, maxSize=50*1024, grow=True,
                                  btr=True, lv=True, encrypted=True),
@@ -112,14 +112,21 @@ class BaseInstallClass(object):
                                  size=500, grow=True, requiredSpace=50*1024,
                                  btr=True, lv=True, encrypted=True)]
 
-        bootreq = platform.setDefaultPartitioning()
-        if bootreq:
-            autorequests.extend(bootreq)
+        bootreqs = storage.platform.setDefaultPartitioning()
+        if bootreqs:
+            autorequests.extend(bootreqs)
 
         (minswap, maxswap) = iutil.swapSuggestion()
         autorequests.append(PartSpec(fstype="swap", size=minswap, maxSize=maxswap,
                                      grow=True, lv=True, encrypted=True))
 
+        for autoreq in autorequests:
+            if autoreq.fstype is None:
+                if autoreq.mountpoint == "/boot":
+                    autoreq.fstype = storage.defaultBootFSType
+                else:
+                    autoreq.fstype = storage.defaultFSType
+
         storage.autoPartitionRequests = autorequests
 
     def configure(self, anaconda):
diff --git a/pyanaconda/installclasses/fedora.py b/pyanaconda/installclasses/fedora.py
index e2c1543..ae1abf0 100644
--- a/pyanaconda/installclasses/fedora.py
+++ b/pyanaconda/installclasses/fedora.py
@@ -76,9 +76,7 @@ class InstallClass(BaseInstallClass):
 
     def configure(self, anaconda):
 	BaseInstallClass.configure(self, anaconda)
-        BaseInstallClass.setDefaultPartitioning(self,
-                                                anaconda.storage,
-                                                anaconda.platform)
+        BaseInstallClass.setDefaultPartitioning(self, anaconda.storage)
 
     def setGroupSelection(self, anaconda):
         BaseInstallClass.setGroupSelection(self, anaconda)
diff --git a/pyanaconda/installclasses/rhel.py b/pyanaconda/installclasses/rhel.py
index 339b5b8..0e36d26 100644
--- a/pyanaconda/installclasses/rhel.py
+++ b/pyanaconda/installclasses/rhel.py
@@ -55,9 +55,7 @@ class InstallClass(BaseInstallClass):
 
     def configure(self, anaconda):
         BaseInstallClass.configure(self, anaconda)
-        BaseInstallClass.setDefaultPartitioning(self,
-                                                anaconda.storage,
-                                                anaconda.platform)
+        BaseInstallClass.setDefaultPartitioning(self, anaconda.storage)
 
     def productMatches(self, oldprod):
         if oldprod is None:
diff --git a/pyanaconda/kickstart.py b/pyanaconda/kickstart.py
index 7e2022a..ed5361a 100644
--- a/pyanaconda/kickstart.py
+++ b/pyanaconda/kickstart.py
@@ -25,6 +25,7 @@ from storage.devicelibs.lvm import getPossiblePhysicalExtents
 from storage.devicelibs.mpath import MultipathConfigWriter, MultipathTopology
 from storage.formats import getFormat
 from storage.partitioning import doPartitioning
+from storage.partitioning import growLVM
 import storage.iscsi
 import storage.fcoe
 import storage.zfcp
@@ -234,7 +235,6 @@ class Authconfig(commands.authconfig.FC3_Authconfig):
 
 class AutoPart(commands.autopart.F17_AutoPart):
     def execute(self, storage, ksdata, instClass):
-        from pyanaconda.platform import getPlatform
         from pyanaconda.storage.partitioning import doAutoPartition
 
         if not self.autopart:
@@ -242,7 +242,7 @@ class AutoPart(commands.autopart.F17_AutoPart):
 
         # sets up default autopartitioning.  use clearpart separately
         # if you want it
-        instClass.setDefaultPartitioning(storage, getPlatform())
+        instClass.setDefaultPartitioning(storage)
         storage.doAutoPart = True
 
         if self.encrypted:
@@ -537,6 +537,9 @@ class LogVol(commands.logvol.F17_LogVol):
         for l in self.lvList:
             l.execute(storage, ksdata, instClass)
 
+        if self.lvList:
+            growLVM(storage.vgs)
+
 class LogVolData(commands.logvol.F17_LogVolData):
     def execute(self, storage, ksdata, instClass):
         devicetree = storage.devicetree
@@ -843,6 +846,9 @@ class Partition(commands.partition.F17_Partition):
         for p in self.partitions:
             p.execute(storage, ksdata, instClass)
 
+        if self.partitions:
+            doPartitioning(storage.disks, storage.partitions, storage.compareDisks)
+
 class PartitionData(commands.partition.F17_PartData):
     def execute(self, storage, ksdata, instClass):
         devicetree = storage.devicetree
@@ -1031,13 +1037,6 @@ class PartitionData(commands.partition.F17_PartData):
             except KeyError:
                 pass
 
-            if "format" in kwargs:
-                # set weight based on fstype and mountpoint
-                mpt = getattr(kwargs["format"], "mountpoint", None)
-                fstype = kwargs["format"].type
-                kwargs["weight"] = storage.platform.weight(fstype=fstype,
-                                                           mountpoint=mpt)
-
             request = storage.newPartition(**kwargs)
             storage.createDevice(request)
 
@@ -1579,5 +1578,4 @@ def doKickstartStorage(storage, ksdata, instClass):
     ksdata.logvol.execute(storage, ksdata, instClass)
     ksdata.btrfs.execute(storage, ksdata, instClass)
     storage.setUpBootLoader()
-    doPartitioning(storage)
 
diff --git a/pyanaconda/platform.py b/pyanaconda/platform.py
index 9abf0f5..7eb05af 100644
--- a/pyanaconda/platform.py
+++ b/pyanaconda/platform.py
@@ -310,8 +310,6 @@ class S390(Platform):
         """Return the default platform-specific partitioning information."""
         from storage.partspec import PartSpec
         return [PartSpec(mountpoint="/boot", size=500,
-#TODO: need to pass this info in w/o using anaconda object
-#                        fstype=self.anaconda.storage.defaultBootFSType,
                          weight=self.weight(mountpoint="/boot"), lv=True,
                          singlePV=True)]
 
diff --git a/pyanaconda/storage/__init__.py b/pyanaconda/storage/__init__.py
index f8740d8..a711149 100644
--- a/pyanaconda/storage/__init__.py
+++ b/pyanaconda/storage/__init__.py
@@ -37,6 +37,7 @@ from pykickstart.constants import *
 from pyanaconda.flags import flags
 from pyanaconda import tsort
 from pyanaconda.errors import *
+from pyanaconda.bootloader import BootLoaderError
 
 from errors import *
 from devices import *
@@ -352,7 +353,6 @@ class Storage(object):
         self.autoPartAddBackupPassphrase = False
         self.encryptionRetrofit = False
         self.autoPartitionRequests = []
-        self.shrinkPartitions = {}
         self.eddDict = {}
 
         self.__luksDevs = {}
@@ -1091,6 +1091,15 @@ class Storage(object):
         else:
             name = "req%d" % self.nextID
 
+        if "weight" not in kwargs:
+            fmt = kwargs.get("format")
+            if fmt:
+                mountpoint = getattr(fmt, "mountpoint", None)
+
+                kwargs["weight"] = self.platform.weight(mountpoint=mountpoint,
+                                                        fstype=fmt.type)
+
+
         return PartitionDevice(name, *args, **kwargs)
 
     def newMDArray(self, *args, **kwargs):
@@ -1115,10 +1124,6 @@ class Storage(object):
     def newVG(self, *args, **kwargs):
         """ Return a new LVMVolumeGroupDevice instance. """
         pvs = kwargs.pop("parents", [])
-        for pv in pvs:
-            if pv not in self.devices:
-                raise ValueError("pv is not in the device tree")
-
         if kwargs.has_key("name"):
             name = kwargs.pop("name")
         else:
@@ -1131,6 +1136,7 @@ class Storage(object):
 
             name = self.suggestContainerName(hostname=hostname, prefix="vg")
 
+        # this may need to go
         if name in self.names:
             raise ValueError("name already in use")
 
@@ -1156,6 +1162,7 @@ class Storage(object):
                                           swap=swap,
                                           mountpoint=mountpoint)
 
+        # this may need to go
         if name in self.names:
             raise ValueError("name already in use")
 
@@ -1207,6 +1214,7 @@ class Storage(object):
             fmt_kwargs["label"] = name
 
         # do we want to prevent a subvol with a name that matches another dev?
+        # this may need to go
         if name in self.names:
             raise ValueError("name already in use")
 
@@ -1241,6 +1249,12 @@ class Storage(object):
         action = ActionDestroyDevice(device)
         self.devicetree.registerAction(action)
 
+    def destroyObsoleteExtended(self, device):
+        if device.exists:
+            self.destroyDevice(device)
+        else:
+            self.devicetree._removeDevice(device, moddisk=False)
+
     def formatDevice(self, device, format):
         """ Schedule formatting of a device. """
         self.devicetree.registerAction(ActionDestroyFormat(device))
@@ -1735,7 +1749,10 @@ class Storage(object):
 
         self.bootloader.stage1_disk = self.devicetree.resolveDevice(self.data.bootloader.bootDrive)
         self.bootloader.stage2_device = self.bootDevice
-        self.bootloader.set_stage1_device(self.devices)
+        try:
+            self.bootloader.set_stage1_device(self.devices)
+        except BootLoaderError as e:
+            log.debug("failed to set bootloader stage1 device: %s" % e)
 
     @property
     def bootDisk(self):
@@ -1753,6 +1770,10 @@ class Storage(object):
         return dev
 
     @property
+    def bootLoaderDevice(self):
+        return getattr(self.bootloader, "stage1_device", None)
+
+    @property
     def bootFSTypes(self):
         """A list of all valid filesystem types for the boot partition."""
         fstypes = []
@@ -1874,8 +1895,50 @@ class Storage(object):
         disks = kwargs.get("disks")
         encrypted = kwargs.get("encrypted", self.data.autopart.encrypted)
 
-        # XXX temporary?
-        size = float(size.convertTo(spec="mb"))
+        # isolated usage
+        isolate = kwargs.get("isolate", False)
+        containers = kwargs.get("containers")
+        add_cb = kwargs.get("add_cb")
+        remove_cb = kwargs.get("remove_ext_cb")
+        partitions = kwargs.get("partitions")
+        size_sets = kwargs.get("size_sets")
+
+        if isolate and (not disks or partitions is None):
+            raise ValueError("missing disk or partition list")
+
+        if isolate and device_type != AUTOPART_TYPE_PLAIN and \
+           containers is None:
+            raise ValueError("missing container list")
+
+        if add_cb is None:
+            if isolate:
+                add_cb = lambda d: None
+            else:
+                add_cb = self.createDevice
+
+        if remove_cb is None:
+            if isolate:
+                remove_cb = lambda d: None
+            else:
+                remove_cb = self.destroyObsoleteExtended
+
+        if size_sets is None:
+            if isolate:
+                size_sets = []
+            else:
+                size_sets = self.size_sets
+
+        if partitions is None:
+            if isolate:
+                partitions = []
+            else:
+                partitions = self.partitions
+
+        if disks is None:
+            if isolate:
+                disks = []
+            else:
+                disks = self.partitioned
 
         # md, btrfs
         raid_level = kwargs.get("level")
@@ -1898,20 +1961,24 @@ class Storage(object):
         # TODO: striping, mirroring, &c
         # TODO: non-partition members (pv-on-md)
         if device_type == AUTOPART_TYPE_PLAIN:
-            # XXX FIXME: for a size of 500MB this is growing unbounded
             device = self.newPartition(grow=True, maxsize=size,
                                        fmt_type=fstype, mountpoint=mountpoint)
-            self.createDevice(device)
+            add_cb(device)
+            partitions.append(device)
             try:
-                doPartitioning(self)
+                doPartitioning(disks, partitions, self.compareDisks,
+                               boot_disk_name=getattr(self.bootDisk,
+                                                      "name", None),
+                               add_cb=add_cb, remove_cb=remove_cb)
             except StorageError as e:
                 log.error("failed to allocate partitions: %s" % e)
-                actions = self.devicetree.findActions(device=device)
-                for a in reversed(actions):
-                    self.devicetree.cancelAction(a)
+                if not isolate:
+                    actions = self.devicetree.findActions(device=device)
+                    for a in reversed(actions):
+                        self.devicetree.cancelAction(a)
             else:
                 # fix the sizes of all allocated partitions
-                for partition in self.partitions:
+                for partition in partitions:
                     partition.req_grow = False
                     partition.req_base_size = partition.size
                     partition.req_size = partition.size
@@ -1965,10 +2032,15 @@ class Storage(object):
             else:
                 set_class = TotalSizeSet
 
+        if containers is not None:
+            container_list = containers
+
+        if container_list is not None:
+            log.debug("newUIDevice: containers=%s" % [(d.name, d.id) for d in container_list])
         container = None
-        containers = [c for c in container_list if not c.exists]
-        if containers:
-            container = containers[0]
+        new_containers = [c for c in container_list if not c.exists]
+        if new_containers:
+            container = new_containers[0]
 
         # If we have a container and also a disk list, how do we reconcile the
         # two to arrive at a member device count?
@@ -1976,11 +2048,10 @@ class Storage(object):
         # at a time.
 
         # set up member devices
-        disks = disks
         container_size = 0
         if container:
             log.debug("using container %s with %d devices" % (container.name,
-                                len(self.devicetree.getChildren(container))))
+                                container.kids))
             container_size = container_size_func(container)
             log.debug("raw container size reported as %d" % container_size)
             disks = list(set([d for m in container.parents for d in m.disks]))
@@ -2004,9 +2075,9 @@ class Storage(object):
                 member.req_size = member.req_base_size
                 member.req_grow = True
 
-                for ss in self.size_sets[:]:
+                for ss in size_sets[:]:
                     if member in ss.devices:
-                        self.size_sets.remove(ss)
+                        size_sets.remove(ss)
         else:
             # set up members as needed to accommodate the device
             members = []
@@ -2017,33 +2088,38 @@ class Storage(object):
 
                 member = self.newPartition(parents=[disk], grow=True,
                                            fmt_type=member_format)
-                self.createDevice(member)
+                add_cb(member)
+                partitions.append(member)
                 if encrypted:
                     fmt = getFormat(luks_format_type)
                     member = LUKSDevice("luks-%s" % member.name,
                                         parents=[member], format=fmt)
-                    self.createDevice(member)
+                    add_cb(member)
 
                 members.append(member)
 
         log.debug("adding a %s with size %d" % (set_class.__name__,
                                                 container_size))
         size_set = set_class(members, container_size)
-        self.size_sets.append(size_set)
+        size_sets.append(size_set)
 
         try:
-            doPartitioning(self)
+            doPartitioning(disks, partitions, self.compareDisks,
+                           boot_disk_name=getattr(self.bootDisk, "name", None),
+                           add_cb=add_cb, remove_cb=remove_cb,
+                           size_sets=size_sets)
         except StorageError as e:
             log.error("UI: failed to allocate partitions: %s" % e)
-            for device in members:
-                actions = self.devicetree.findActions(device=device)
-                for a in reversed(actions):
-                    self.devicetree.cancelAction(a)
+            if not isolate:
+                for device in members:
+                    actions = self.devicetree.findActions(device=device)
+                    for a in reversed(actions):
+                        self.devicetree.cancelAction(a)
             # FIXME: revert container to its original state
             return
         else:
             # fix the sizes of all allocated partitions
-            for partition in self.partitions:
+            for partition in partitions:
                 partition.req_grow = False
                 partition.req_base_size = partition.size
                 partition.req_size = partition.size
@@ -2054,7 +2130,7 @@ class Storage(object):
         if not container and new_container:
             log.debug("creating new container")
             container = new_container(parents=parents)
-            self.createDevice(container)
+            add_cb(container)
 
         if container:
             parents = [container]
@@ -2073,7 +2149,21 @@ class Storage(object):
                                 size=size,
                                 fmt_type=fstype,
                                 mountpoint=mountpoint)
-            self.createDevice(device)
+            add_cb(device)
+
+    def copy(self):
+        import copy
+        new = copy.deepcopy(self)
+        # go through and re-get partedPartitions from the disks since they
+        # don't get deep-copied
+        for partition in new.partitions:
+            if not partition._partedPartition:
+                continue
+
+            p = partition.disk.format.partedDisk.getPartitionByPath(partition.path)
+            partition.partedPartition = p
+
+        return new
 
 def mountExistingSystem(fsset, rootEnt,
                         allowDirty=None, dirtyCB=None,
diff --git a/pyanaconda/storage/devicelibs/btrfs.py b/pyanaconda/storage/devicelibs/btrfs.py
index 3fe1218..3355ead 100644
--- a/pyanaconda/storage/devicelibs/btrfs.py
+++ b/pyanaconda/storage/devicelibs/btrfs.py
@@ -32,24 +32,21 @@ _ = lambda x: gettext.ldgettext("anaconda", x)
 import logging
 log = logging.getLogger("storage")
 
-def btrfs(args, progress=None, capture=False):
+def btrfs(args, capture=False):
     kwargs = {}
     kwargs["stderr"] = "/dev/tty5"
     if capture:
-        # execWithCapture can't take a progress window so it gets ignored
         exec_func = iutil.execWithCapture
     else:
-        exec_func = iutil.execWithPulseProgress
+        exec_func = iutil.execWithRedirect
         kwargs["stdout"] = "/dev/tty5"
-        kwargs["progress"] = progress
 
     ret = exec_func("btrfs", args, **kwargs)
-    if not capture and ret.rc:
-        raise BTRFSError(ret.stderr)
-
+    #if ret:
+    #    raise BTRFSError(ret)
     return ret
 
-def create_volume(devices, label=None, data=None, metadata=None, progress=None):
+def create_volume(devices, label=None, data=None, metadata=None):
     """ For now, data and metadata must be strings mkfs.btrfs understands. """
     if not devices:
         raise ValueError("no devices specified")
@@ -68,11 +65,10 @@ def create_volume(devices, label=None, data=None, metadata=None, progress=None):
 
     args.extend(devices)
 
-    ret = iutil.execWithPulseProgress("mkfs.btrfs", args,
-                                      stdout="/dev/tty5", stderr="/dev/tty5",
-                                      progress=progress)
-    if ret.rc:
-        raise BTRFSError(ret.stderr)
+    ret = iutil.execWithRedirect("mkfs.btrfs", args,
+                                 stdout="/dev/tty5", stderr="/dev/tty5")
+    #if ret:
+    #    raise BTRFSError(ret)
 
     return ret
 
@@ -82,21 +78,21 @@ def create_volume(devices, label=None, data=None, metadata=None, progress=None):
 
 # remove device
 
-def create_subvolume(mountpoint, name, progress=None):
+def create_subvolume(mountpoint, name):
     if not os.path.ismount(mountpoint):
         raise ValueError("volume not mounted")
 
     path = os.path.normpath("%s/%s" % (mountpoint, name))
     args = ["subvol", "create", path]
-    return btrfs(args, progress=progress)
+    return btrfs(args)
 
-def delete_subvolume(mountpoint, name, progress=None):
+def delete_subvolume(mountpoint, name):
     if not os.path.ismount(mountpoint):
         raise ValueError("volume not mounted")
 
     path = os.path.normpath("%s/%s" % (mountpoint, name))
     args = ["subvol", "delete", path]
-    return btrfs(args, progress=progress)
+    return btrfs(args)
 
 def create_snapshot(source, dest):
     pass
diff --git a/pyanaconda/storage/formats/disklabel.py b/pyanaconda/storage/formats/disklabel.py
index 338a880..87dbea2 100644
--- a/pyanaconda/storage/formats/disklabel.py
+++ b/pyanaconda/storage/formats/disklabel.py
@@ -85,10 +85,13 @@ class DiskLabel(DeviceFormat):
         """
         new = self.__class__.__new__(self.__class__)
         memo[id(self)] = new
-        shallow_copy_attrs = ('_partedDevice', '_partedDisk', '_origPartedDisk')
+        shallow_copy_attrs = ('_partedDevice', '_alignment', '_endAlignment')
+        duplicate_attrs = ('_partedDisk', '_origPartedDisk')
         for (attr, value) in self.__dict__.items():
             if attr in shallow_copy_attrs:
                 setattr(new, attr, copy.copy(value))
+            elif attr in duplicate_attrs:
+                setattr(new, attr, value.duplicate())
             else:
                 setattr(new, attr, copy.deepcopy(value, memo))
 
diff --git a/pyanaconda/storage/partitioning.py b/pyanaconda/storage/partitioning.py
index 3b55f99..c844c13 100644
--- a/pyanaconda/storage/partitioning.py
+++ b/pyanaconda/storage/partitioning.py
@@ -41,14 +41,10 @@ _ = lambda x: gettext.ldgettext("anaconda", x)
 import logging
 log = logging.getLogger("storage")
 
-def _getCandidateDisks(storage):
+def _getContainerMemberDisks(disks):
     """ Return a list of disks with space for a default-sized partition. """
-    disks = []
-    for disk in storage.partitioned:
-        if storage.config.clearPartDisks and \
-           (disk.name not in storage.config.clearPartDisks):
-            continue
-
+    usable_disks = []
+    for disk in disks:
         part = disk.format.firstPartition
         while part:
             if not part.type & parted.PARTITION_FREESPACE:
@@ -56,45 +52,49 @@ def _getCandidateDisks(storage):
                 continue
 
             if part.getSize(unit="MB") > PartitionDevice.defaultSize:
-                disks.append(disk)
+                usable_disks.append(disk)
                 break
 
             part = part.nextPartition()
 
-    return disks
+    log.debug("container disks: %s" % [d.name for d in usable_disks])
+    return usable_disks
 
-def _scheduleImplicitPartitions(storage, disks):
-    """ Schedule creation of a lvm/btrfs partition on each disk in disks. """
+def _createContainerMemberPartitions(disks, autopart_type, new_partition_func,
+                                     encrypted, encryption_kwargs):
+    """ Create an lvm/btrfs partition on each disk in disks. """
     # create a separate pv or btrfs partition for each disk with free space
     devs = []
 
-    # only schedule the partitions if either lvm or btrfs autopart was chosen
-    if storage.autoPartType not in (AUTOPART_TYPE_LVM, AUTOPART_TYPE_BTRFS):
+    log.debug("creating container member partitions: %s" % [d.name for d in disks])
+
+    # only create these partitions if either lvm or btrfs autopart was chosen
+    if autopart_type not in (AUTOPART_TYPE_LVM, AUTOPART_TYPE_BTRFS):
         return devs
 
-    for disk in disks:
-        if storage.encryptedAutoPart:
+    # filter out disks that don't have enough space for a new default-sized
+    # partition
+    for disk in _getContainerMemberDisks(disks):
+        if encrypted:
             fmt_type = "luks"
-            fmt_args = {"passphrase": storage.encryptionPassphrase,
-                        "escrow_cert": storage.autoPartEscrowCert,
-                        "add_backup_passphrase": storage.autoPartAddBackupPassphrase}
+            fmt_args = encryption_kwargs
         else:
-            if storage.autoPartType == AUTOPART_TYPE_LVM:
+            if autopart_type == AUTOPART_TYPE_LVM:
                 fmt_type = "lvmpv"
             else:
                 fmt_type = "btrfs"
             fmt_args = {}
-        part = storage.newPartition(fmt_type=fmt_type,
+        part = new_partition_func(fmt_type=fmt_type,
                                                 fmt_args=fmt_args,
                                                 grow=True,
                                                 parents=[disk])
-        storage.createDevice(part)
         devs.append(part)
 
     return devs
 
-def _schedulePartitions(storage, disks):
-    """ Schedule creation of autopart partitions. """
+def _createPartitions(disks, autopart_type, autopart_reqs, new_partition_func,
+                        encrypted, encryption_kwargs, stage1_device=None):
+    """ Create autopart partitions. """
     # basis for requests with requiredSpace is the sum of the sizes of the
     # two largest free regions
     all_free = getFreeRegions(disks)
@@ -109,35 +109,19 @@ def _schedulePartitions(storage, disks):
     if len(all_free) > 1:
         free += all_free[1].getSize()
 
-    # The boot disk must be set at this point. See if any platform-specific
-    # stage1 device we might allocate already exists on the boot disk.
-    stage1_device = None
-    for device in storage.devices:
-        if storage.bootloader.stage1_disk not in device.disks:
-            continue
-
-        if storage.bootloader.is_valid_stage1_device(device):
-            stage1_device = device
-            break
-
     #
     # First pass is for partitions only. We'll do LVs later.
     #
-    for request in storage.autoPartitionRequests:
-        if (request.lv and storage.autoPartType == AUTOPART_TYPE_LVM) or \
-           (request.btr and storage.autoPartType == AUTOPART_TYPE_BTRFS):
+    devices = []
+    for request in autopart_reqs:
+        if (request.lv and autopart_type == AUTOPART_TYPE_LVM) or \
+           (request.btr and autopart_type == AUTOPART_TYPE_BTRFS):
             continue
 
         if request.requiredSpace and request.requiredSpace > free:
             continue
 
-        if request.fstype is None:
-            if request.mountpoint == "/boot":
-                request.fstype = storage.defaultBootFSType
-            else:
-                request.fstype = storage.defaultFSType
-
-        elif request.fstype in ("prepboot", "efi", "biosboot", "hfs+") and \
+        if request.fstype in ("prepboot", "efi", "biosboot", "hfs+") and \
              stage1_device:
             # there should never be a need for more than one of these
             # partitions, so skip them.
@@ -153,19 +137,18 @@ def _schedulePartitions(storage, disks):
 
         # This is a little unfortunate but let the backend dictate the rootfstype
         # so that things like live installs can do the right thing
-        if request.mountpoint == "/" and storage.liveImage:
-            request.fstype = storage.liveImage.format.type
+        # XXX this is bullshit
+        #if request.mountpoint == "/" and storage.liveImage:
+        #    request.fstype = storage.liveImage.format.type
 
-        if request.encrypted and storage.encryptedAutoPart:
+        if request.encrypted and encrypted:
             fmt_type = "luks"
-            fmt_args = {"passphrase": storage.encryptionPassphrase,
-                        "escrow_cert": storage.autoPartEscrowCert,
-                        "add_backup_passphrase": storage.autoPartAddBackupPassphrase}
+            fmt_args = encryption_kwargs
         else:
             fmt_type = request.fstype
             fmt_args = {}
 
-        dev = storage.newPartition(fmt_type=fmt_type,
+        dev = new_partition_func(fmt_type=fmt_type,
                                             fmt_args=fmt_args,
                                             size=request.size,
                                             grow=request.grow,
@@ -174,60 +157,55 @@ def _schedulePartitions(storage, disks):
                                             parents=disks,
                                             weight=request.weight)
 
-        # schedule the device for creation
-        storage.createDevice(dev)
+        devices.append(dev)
 
-        if request.encrypted and storage.encryptedAutoPart:
+        if request.encrypted and encrypted:
             luks_fmt = getFormat(request.fstype,
                                  device=dev.path,
                                  mountpoint=request.mountpoint)
             luks_dev = LUKSDevice("luks-%s" % dev.name,
                                   format=luks_fmt,
                                   size=dev.size,
-                                  parents=dev)
-            storage.createDevice(luks_dev)
+                                  parents=[dev])
+
+            devices.append(luks_dev)
 
-    # make sure preexisting broken lvm/raid configs get out of the way
-    return
+    return devices
 
-def _scheduleVolumes(storage, devs):
-    """ Schedule creation of autopart lvm/btrfs volumes. """
-    if not devs:
+def _createVolumes(members, autopart_type, autopart_reqs,
+                   new_container_func, new_volume_func):
+    """ Create autopart lvm/btrfs volumes. """
+    if not members or \
+       autopart_type not in (AUTOPART_TYPE_LVM, AUTOPART_TYPE_BTRFS):
         return
 
-    if storage.autoPartType == AUTOPART_TYPE_LVM:
-        new_container = storage.newVG
-        new_volume = storage.newLV
-        format_name = "lvmpv"
-    else:
-        new_container = storage.newBTRFS
-        new_volume = storage.newBTRFS
-        format_name = "btrfs"
+    parents = []
+    for dev in members:
+        if dev.format.type == "luks":
+            if autopart_type == AUTOPART_TYPE_LVM:
+                fmt_type = "lvmpv"
+            else:
+                fmt_type = "btrfs"
 
-    if storage.encryptedAutoPart:
-        pvs = []
-        for dev in devs:
             pv = LUKSDevice("luks-%s" % dev.name,
-                            format=getFormat(format_name, device=dev.path),
+                            format=getFormat(fmt_type, device=dev.path),
                             size=dev.size,
                             parents=dev)
-            pvs.append(pv)
-            storage.createDevice(pv)
-    else:
-        pvs = devs
+        else:
+            pv = dev
+
+        parents.append(pv)
 
     # create a vg containing all of the autopart pvs
-    container = new_container(parents=pvs)
-    storage.createDevice(container)
+    container = new_container_func(parents=parents)
 
     #
-    # Convert storage.autoPartitionRequests into Device instances and
-    # schedule them for creation.
+    # Convert storage.autoPartitionRequests into Device instances.
     #
     # Second pass, for LVs only.
-    for request in storage.autoPartitionRequests:
-        btr = storage.autoPartType == AUTOPART_TYPE_BTRFS and request.btr
-        lv = storage.autoPartType == AUTOPART_TYPE_LVM and request.lv
+    for request in autopart_reqs:
+        btr = autopart_type == AUTOPART_TYPE_BTRFS and request.btr
+        lv = autopart_type == AUTOPART_TYPE_LVM and request.lv
 
         if not btr and not lv:
             continue
@@ -237,90 +215,177 @@ def _scheduleVolumes(storage, devs):
            request.requiredSpace and request.requiredSpace > container.size:
             continue
 
-        if request.fstype is None:
-            if btr:
-                # btrfs volumes can only contain btrfs filesystems
-                request.fstype = "btrfs"
-            else:
-                request.fstype = storage.defaultFSType
+        # don't modify the request so we can change autopart type between
+        # autopart runs
+        fstype = request.fstype
+        if btr:
+            # btrfs volumes can only contain btrfs filesystems
+            fstype = "btrfs"
 
         # This is a little unfortunate but let the backend dictate the rootfstype
         # so that things like live installs can do the right thing
         # XXX FIXME: yes, unfortunate. Disallow btrfs autopart on live install.
-        if request.mountpoint == "/" and storage.liveImage:
-            if btr:
-                raise PartitioningError("live install can't do btrfs autopart")
+        #if request.mountpoint == "/" and storage.liveImage:
+        #    if btr:
+        #        raise PartitioningError("live install can't do btrfs autopart")
 
-            request.fstype = storage.liveImage.format.type
+        #    fstype = storage.liveImage.format.type
 
         kwargs = {"mountpoint": request.mountpoint,
-                  "fmt_type": request.fstype}
+                  "fmt_type": fstype,
+                  "parents": [container]}
         if lv:
-            kwargs.update({"parents": [container],
-                           "grow": request.grow,
+            kwargs.update({"grow": request.grow,
                            "maxsize": request.maxSize,
                            "size": request.size,
                            "singlePV": request.singlePV})
         else:
-            kwargs.update({"parents": [container],
-                           "subvol": True})
-
-        dev = new_volume(**kwargs)
-
-        # schedule the device for creation
-        storage.createDevice(dev)
-
-def scheduleShrinkActions(storage):
-    """ Schedule actions to shrink partitions as per autopart selection. """
-    for (path, size) in storage.shrinkPartitions.items():
-        device = storage.devicetree.getDeviceByPath(path, preferLeaves=False)
-        if not device or not isinstance(device, PartitionDevice):
-            raise StorageError("device %s scheduled for shrink disappeared"
-                                % path)
-        storage.devicetree.registerAction(ActionResizeFormat(device, size))
-        storage.devicetree.registerAction(ActionResizeDevice(device, size))
-        # aligning the partition's new end sector may have changed the size
-        if device.targetSize != size:
-            device.format.targetSize = device.targetSize
-
-def doAutoPartition(storage, data):
+            kwargs.update({"subvol": True})
+
+        dev = new_volume_func(**kwargs)
+
+    return container
+
+def doAutoPartition(storage, data, disks=None, partitions=None, vgs=None,
+                    add_cb=None, remove_ext_cb=None, isolate=False):
+    """ Set up automatic partitioning.
+
+        Arguments:
+
+            storage         a pyanaconda.storage.Storage instance
+            data            a pykickstart.BaseHandler instance
+
+        Keyword arguments:
+
+            disks           set of disks to use instead of storage.partitioned
+            partitions      set of partitions to use instead of
+                            storage.partitions
+            vgs             set of vgs to use instead of storage.vgs
+
+            add_cb          callback to use instead of storage.createDevice
+            remove_ext_cb   callback to use instead of
+                            storage.destroyObsoleteExtended
+
+            isolate         whether to treat storage instance as read-only
+
+    """
     log.debug("doAutoPart: %s" % storage.doAutoPart)
     log.debug("encryptedAutoPart: %s" % storage.encryptedAutoPart)
     log.debug("autoPartType: %s" % storage.autoPartType)
     log.debug("clearPartType: %s" % storage.config.clearPartType)
     log.debug("clearPartDisks: %s" % storage.config.clearPartDisks)
     log.debug("autoPartitionRequests:\n%s" % "".join([str(p) for p in storage.autoPartitionRequests]))
-    log.debug("storage.disks: %s" % [d.name for d in storage.disks])
-    log.debug("storage.partitioned: %s" % [d.name for d in storage.partitioned])
-    log.debug("all names: %s" % [d.name for d in storage.devices])
+
+    if not isolate:
+        log.debug("all names: %s" % [d.name for d in storage.devices])
+
     log.debug("boot disk: %s" % getattr(storage.bootDisk, "name", None))
 
-    disks = []
-    devs = []
+    if disks is None:
+        disks = storage.partitioned
 
-    if storage.doAutoPart:
-        # XXX this doesn't belong on newui
-        scheduleShrinkActions(storage)
+    log.debug("disks: %s" % [d.name for d in disks])
 
-        disks = _getCandidateDisks(storage)
-        devs = _scheduleImplicitPartitions(storage, disks)
-        log.debug("candidate disks: %s" % disks)
-        log.debug("devs: %s" % devs)
+    if vgs is None:
+        vgs = storage.vgs
 
-        if disks == []:
-            raise NoDisksError
+    if partitions is None:
+        partitions = storage.partitions
 
-        _schedulePartitions(storage, disks)
+    # set defaults for the add and remove callbacks
+    if not add_cb:
+        if isolate:
+            add_cb = lambda d: None
+        else:
+            add_cb = storage.createDevice
+
+    if not remove_ext_cb:
+        if isolate:
+            remove_ext_cb = lambda d: None
+        else:
+            remove_ext_cb = storage.destroyObsoleteExtended
+
+    # establish bootloader stage1, stage2 devices if possible
+    storage.setUpBootLoader()
+
+    encryption_kwargs = {"passphrase": storage.encryptionPassphrase,
+                         "escrow_cert": storage.autoPartEscrowCert,
+                         "add_backup_passphrase": storage.autoPartAddBackupPassphrase}
+
+    # start with disks allowable for use in autopart
+    use_disks = []
+    for disk in disks:
+        if not storage.config.clearPartDisks or \
+           disk.name not in storage.config.clearPartDisks:
+            continue
+
+        use_disks.append(disk)
+
+    if use_disks == []:
+        raise NoDisksError
+
+    log.debug("candidate disks: %s" % [d.name for d in use_disks])
+    members = _createContainerMemberPartitions(use_disks,
+                                               data.autopart.type,
+                                               storage.newPartition,
+                                               data.autopart.encrypted,
+                                               encryption_kwargs)
+
+    log.debug("members: %s" % [d.name for d in members])
+    for dev in members:
+        add_cb(dev)
+        if dev.type == "partition" and dev not in partitions:
+            partitions.append(dev)
+
+    devices = _createPartitions(use_disks,
+                                data.autopart.type,
+                                storage.autoPartitionRequests,
+                                storage.newPartition,
+                                data.autopart.encrypted,
+                                encryption_kwargs,
+                                storage.bootLoaderDevice)
+
+    for dev in devices:
+        add_cb(dev)
+        if dev.type == "partition" and dev not in partitions:
+            partitions.append(dev)
 
     # run the autopart function to allocate and grow partitions
     try:
-        doPartitioning(storage)
+        doPartitioning(disks, partitions, storage.compareDisks,
+                       boot_disk_name=data.bootloader.bootDrive,
+                       add_cb=add_cb, remove_cb=remove_ext_cb)
 
-        if storage.doAutoPart:
-            _scheduleVolumes(storage, devs)
-
-        # grow LVs
-        growLVM(storage)
+        if storage.autoPartType == AUTOPART_TYPE_LVM:
+            new_container_func = storage.newVG
+            new_volume_func = storage.newLV
+        else:
+            new_container_func = storage.newBTRFS
+            new_volume_func = storage.newBTRFS
+
+        container = _createVolumes(members,
+                                   data.autopart.type,
+                                   storage.autoPartitionRequests,
+                                   new_container_func,
+                                   new_volume_func)
+
+        if container:
+            add_cb(container)
+            volumes = []
+            if hasattr(container, "lvs"):
+                volumes = container.lvs
+            elif hasattr(container, "subvolumes"):
+                volumes = container.subvolumes
+
+            for volume in volumes:
+                add_cb(volume)
+
+        if data.autopart.type == AUTOPART_TYPE_LVM:
+            if container and container not in vgs:
+                vgs.append(container)
+
+            # grow LVs
+            growLVM(vgs)
     except PartitioningWarning as e:
         log.warning(str(e))
         if errorHandler.cb(e) == ERROR_RAISE:
@@ -331,19 +396,20 @@ def doAutoPartition(storage, data):
             storage.reset()
             raise
 
-    storage.setUpBootLoader()
-
-    # now do a full check of the requests
-    (errors, warnings) = storage.sanityCheck()
-    for error in errors:
-        log.error(error)
-    for warning in warnings:
-        log.warning(warning)
-    if errors:
-        exn = PartitioningError("\n".join(errors))
-        if errorHandler.cb(exn) == ERROR_RAISE:
-            storage.reset()
-            raise exn
+    if not isolate:
+        storage.setUpBootLoader()
+
+        # now do a full check of the requests
+        (errors, warnings) = storage.sanityCheck()
+        for error in errors:
+            log.error(error)
+        for warning in warnings:
+            log.warning(warning)
+        if errors:
+            exn = PartitioningError("\n".join(errors))
+            if errorHandler.cb(exn) == ERROR_RAISE:
+                storage.reset()
+                raise exn
 
 def partitionCompare(part1, part2):
     """ More specifically defined partitions come first.
@@ -689,50 +755,54 @@ def getFreeRegions(disks):
 
     return free
 
-def updateExtendedPartitions(storage, disks):
+def updateExtendedPartitions(disks, partitions, add_cb=None, remove_cb=None):
     # XXX hack -- if we created any extended partitions we need to add
     #             them to the tree now
+    if remove_cb is None:
+        remove_cb = lambda d: None
+
+    if add_cb is None:
+        add_cb = lambda d: None
+
     for disk in disks:
-        extended = disk.format.extendedPartition
-        if not extended:
+        parted_extended = disk.format.extendedPartition
+        if not parted_extended:
             # remove any obsolete extended partitions
-            for part in storage.partitions:
+            for part in partitions:
                 if part.disk == disk and part.isExtended:
-                    if part.exists:
-                        storage.destroyDevice(part)
-                    else:
-                        storage.devicetree._removeDevice(part, moddisk=False)
+                    remove_cb(part)
             continue
 
-        extendedName = devicePathToName(extended.getDeviceNodeName())
+        extendedName = devicePathToName(parted_extended.getDeviceNodeName())
         # remove any obsolete extended partitions
-        for part in storage.partitions:
+        for part in partitions:
             if part.disk == disk and part.isExtended and \
                part.partedPartition not in disk.format.partitions:
-                if part.exists:
-                    storage.destroyDevice(part)
-                else:
-                    storage.devicetree._removeDevice(part, moddisk=False)
+                remove_cb(part)
 
-        device = storage.devicetree.getDeviceByName(extendedName)
-        if device:
-            if not device.exists:
+        extended = None
+        for partition in partitions:
+            if partition.name == extendedName:
+                extended = partition
+                break
+
+        if extended:
+            if not extended.exists:
                 # created by us, update partedPartition
-                device.partedPartition = extended
+                extended.partedPartition = parted_extended
             continue
 
         # This is a little odd because normally instantiating a partition
         # that does not exist means leaving self.parents empty and instead
         # populating self.req_disks. In this case, we need to skip past
         # that since this partition is already defined.
-        device = PartitionDevice(extendedName, parents=disk)
-        device.parents = [disk]
-        device.partedPartition = extended
-        # just add the device for now -- we'll handle actions at the last
-        # moment to simplify things
-        storage.devicetree._addDevice(device)
-
-def doPartitioning(storage):
+        extended = PartitionDevice(extendedName, parents=disk)
+        extended.parents = [disk]
+        extended.partedPartition = extended
+        add_cb(extended)
+
+def doPartitioning(disks, partitions, disk_compare_func, size_sets=None,
+                   boot_disk_name=None, add_cb=None, remove_cb=None):
     """ Allocate and grow partitions.
 
         When this function returns without error, all PartitionDevice
@@ -744,19 +814,13 @@ def doPartitioning(storage):
         Arguments:
 
             storage - Main anaconda Storage instance
+            XXX FIXME
 
         Keyword/Optional Arguments:
 
             None
 
     """
-    if not hasattr(storage.platform, "diskLabelTypes"):
-        raise StorageError("can't allocate partitions without platform data")
-
-    disks = storage.partitioned
-    if storage.config.exclusiveDisks:
-        disks = [d for d in disks if d.name in storage.config.exclusiveDisks]
-
     for disk in disks:
         try:
             disk.setup()
@@ -764,44 +828,34 @@ def doPartitioning(storage):
             log.error("failed to set up disk %s: %s" % (name, msg))
             raise PartitioningError("disk %s inaccessible" % disk.name)
 
-    partitions = storage.partitions[:]
-    for part in storage.partitions:
-        part.req_bootable = False
-
-        if part.exists:
-            # if the partition is preexisting or part of a complex device
-            # then we shouldn't modify it
-            partitions.remove(part)
-            continue
-
+    # this can probably go as long as something resets partition req_size
+    for part in partitions:
         if not part.exists:
             # start over with flexible-size requests
             part.req_size = part.req_base_size
 
-    try:
-        storage.bootDevice.req_bootable = True
-    except AttributeError:
-        # there's no stage2 device. hopefully it's temporary.
-        pass
-
     removeNewPartitions(disks, partitions)
     free = getFreeRegions(disks)
     try:
-        allocatePartitions(storage, disks, partitions, free)
-        growPartitions(disks, partitions, free, size_sets=storage.size_sets)
+        allocatePartitions(disks, partitions, free,
+                           disk_compare_func=disk_compare_func,
+                           boot_disk_name=boot_disk_name)
+        growPartitions(disks, partitions, free, size_sets=size_sets)
     finally:
         # The number and thus the name of partitions may have changed now,
         # allocatePartitions() takes care of this for new partitions, but not
         # for pre-existing ones, so we update the name of all partitions here
-        for part in storage.partitions:
+        for part in partitions:
             # leave extended partitions as-is -- we'll handle them separately
             if part.isExtended:
                 continue
             part.updateName()
 
-        updateExtendedPartitions(storage, disks)
+        updateExtendedPartitions(disks, partitions,
+                                 add_cb=add_cb, remove_cb=remove_cb)
 
-def allocatePartitions(storage, disks, partitions, freespace):
+def allocatePartitions(disks, partitions, freespace, disk_compare_func=None,
+                       boot_disk_name=None):
     """ Allocate partitions based on requested features.
 
         Non-existing partitions are sorted according to their requested
@@ -850,9 +904,9 @@ def allocatePartitions(storage, disks, partitions, freespace):
             req_disks = disks
 
         # sort the disks, making sure the boot disk is first
-        req_disks.sort(key=lambda d: d.name, cmp=storage.compareDisks)
+        req_disks.sort(key=lambda d: d.name, cmp=disk_compare_func)
         for disk in req_disks:
-            if storage.bootDisk and disk == storage.bootDisk:
+            if boot_disk_name and disk.name == boot_disk_name:
                 boot_index = req_disks.index(disk)
                 req_disks.insert(0, req_disks.pop(boot_index))
 
@@ -1885,9 +1939,9 @@ def lvCompare(lv1, lv2):
 
     return ret
 
-def growLVM(storage):
+def growLVM(vg_list):
     """ Grow LVs according to the sizes of the PVs. """
-    for vg in storage.vgs:
+    for vg in vg_list:
         total_free = vg.freeSpace
         if total_free < 0:
             # by now we have allocated the PVs so if there isn't enough
diff --git a/pyanaconda/ui/gui/spokes/custom.py b/pyanaconda/ui/gui/spokes/custom.py
index fee70ee..8e70a6f 100644
--- a/pyanaconda/ui/gui/spokes/custom.py
+++ b/pyanaconda/ui/gui/spokes/custom.py
@@ -27,6 +27,8 @@
 # - Newly created devices can not be resized (because self.resizable requires self.exists).
 # - Tabbing behavior in the accordion is weird.
 
+import copy
+
 import gettext
 _ = lambda x: gettext.ldgettext("anaconda", x)
 N_ = lambda x: x
@@ -40,8 +42,7 @@ from pyanaconda.storage.formats import getFormat
 from pyanaconda.storage.size import Size
 from pyanaconda.storage import Root
 from pyanaconda.storage.partitioning import doPartitioning
-from pyanaconda.storage.partitioning import Chunk
-from pyanaconda.storage.partitioning import Request
+from pyanaconda.storage.partitioning import doAutoPartition
 from pyanaconda.storage.errors import StorageError
 
 from pyanaconda.ui.gui import UIObject
@@ -131,47 +132,58 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
 
         self._current_selector = None
         self._when_create_text = ""
-        self._ui_devices = []
+        self._devices = []
         self._deleted_devices = []
         self._free_space = Size(bytes=0)
 
     def apply(self):
         # schedule actions for device removals, resizes
         for removed in self._deleted_devices:
-            if removed.device_id is None:
+            if removed.id not in [d.id for d in self.storage.devices]:
                 # created here, destroyed here -> NOOP
                 continue
 
-            log.debug("ui: removing device %s" % removed.name)
             device = self.storage.devicetree.getDeviceByName(removed.name)
             self.storage.destroyDevice(device)
 
-        for ui_device in self._ui_devices:
-            if ui_device.device_id is None:
+        for ui_device in self._devices:
+            if ui_device not in self.storage.devices:
                 # we're creating a device
 
-                # this isn't a leaf device
-                if ui_device.create_type is None:
+                if not ui_device.isleaf:
                     continue
 
-                self.storage.newUIDevice(ui_device.create_type,
+                mountpoint = getattr(ui_device.format, "mountpoint", None)
+
+                if ui_device.type == "partition":
+                    device_type = AUTOPART_TYPE_PLAIN
+                elif ui_device.type == "lvmlv":
+                    device_type = AUTOPART_TYPE_LVM
+                elif ui_device.type == "btrfs subvolume":
+                    device_type = AUTOPART_TYPE_BTRFS
+                else:
+                    log.error("don't know what to do with '%s'" % ui_device)
+                    continue
+
+                # needs work
+                encrypted = self.data.autopart.encrypted
+                disks = self.storage.partitioned
+
+                self.storage.newUIDevice(device_type,
                                          ui_device.size,
-                                         mountpoint=ui_device.mountpoint,
-                                         fstype=ui_device.fstype,
-                                         encrypted=ui_device.encrypted,
-                                         level=ui_device.raid_level,
-                                         disks=self.storage.disks)
-
-            # this is a device that's already in the tree
-            device = self.storage.devicetree.getDeviceByName(ui_device.name)
-            if ui_device.resizable and ui_device.size != device.size:
-                # schedule a resize action for the device and its format
-                pass
-
-        # XXX How can we handle edits?
-        #
-        #       Doesn't matter for now since we don't have support for editing
-        #       devices.
+                                         mountpoint=mountpoint,
+                                         fstype=ui_device.format.type,
+                                         encrypted=encrypted,
+                                         level=None,
+                                         disks=disks)
+            else:
+                # this is a device that's already in the tree
+                device = self.storage.devicetree.getDeviceByName(ui_device.name)
+                if ui_device.resizable and ui_device.size != device.size:
+                    # schedule a resize action for the device and its format
+                    pass
+
+                # TODO: handle edits to a device in the tree
 
         self.storage.setUpBootLoader()
         StorageChecker.run(self)
@@ -243,8 +255,9 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
         else:
             return None
 
+    @property
     def _clearpartDevices(self):
-        return [d for d in self.storage.devicetree.devices if d.name in self.data.clearpart.drives]
+        return [d for d in self._devices if d.name in self.data.clearpart.drives]
 
     def _unusedDevices(self):
         from pyanaconda.storage.devices import DiskDevice
@@ -252,27 +265,22 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
 
     def _setCurrentFreeSpace(self):
         """Add up all the free space on selected disks and return it as a Size."""
-        totalFree = Size(bytes=0)
-
-        freeDisks = self.storage.getFreeSpace(disks=self._clearpartDevices())
-        for (disk_free, fs_free) in freeDisks.values():
-            # only show actual free space as free -- don't include space that
-            # could be gained by shrinking filesystems
-            totalFree += disk_free
-
-        self._free_space = totalFree
+        freeDisks = self.storage.getFreeSpace(disks=self._clearpartDevices,
+                                              clearPartType=CLEARPART_TYPE_NONE)
+        self._free_space = sum([f[0] for f in freeDisks.values()])
 
     def _currentTotalSpace(self):
         """Add up the sizes of all selected disks and return it as a Size."""
         totalSpace = 0
 
-        for disk in self._clearpartDevices():
+        for disk in self._clearpartDevices:
             totalSpace += disk.size
 
         return Size(spec="%s MB" % totalSpace)
 
     def _updateSpaceDisplay(self):
         # Set up the free space/available space displays in the bottom left.
+        self._setCurrentFreeSpace()
         self._availableSpaceLabel = self.builder.get_object("availableSpaceLabel")
         self._totalSpaceLabel = self.builder.get_object("totalSpaceLabel")
         self._summaryButton = self.builder.get_object("summary_button")
@@ -292,7 +300,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
     def refresh(self):
         NormalSpoke.refresh(self)
         self._deleted_devices = []
-        self._ui_devices = get_ui_devices(self.storage)
+        self._devices = self.storage.copy().devices
         self._do_refresh()
 
         # update our free space number based on Storage
@@ -312,7 +320,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
 
         # set up a dict with name keys for the ui device list
         ui_device_dict = {}
-        for device in self._ui_devices:
+        for device in self._devices:
             ui_device_dict[device.name] = device
 
         # We can only have one page expanded at a time.
@@ -320,8 +328,8 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
 
         unused = [ui_device_dict[d.name] for d in self._unusedDevices()
                     if d.name in ui_device_dict and
-                       ui_device_dict[d.name].device_id is not None]
-        new_devices = [d for d in self._ui_devices if not d.exists]
+                       ui_device_dict[d.name].id == d.id]
+        new_devices = [d for d in self._devices if not d.exists]
 
         log.debug("ui: unused=%s" % [d.name for d in unused])
         log.debug("ui: new_devices=%s" % [d.name for d in new_devices])
@@ -348,7 +356,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
             if not swaps and not mounts:
                 continue
 
-            ui_roots.append(UIRoot(name=name, mounts=mounts, swaps=swaps))
+            ui_roots.append(Root(name=name, mounts=mounts, swaps=swaps))
 
         # If we've not yet run autopart, add an instance of CreateNewPage.  This
         # ensures it's only added once.
@@ -363,9 +371,10 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
             label = self.builder.get_object("whenCreateLabel")
             label.set_text(self._when_create_text % (productName, productVersion))
         else:
-            swaps = [d for d in new_devices if d.fstype == "swap"]
-            mounts = dict([(d.mountpoint, d) for d in new_devices if d.mountpoint])
-            new_root = UIRoot(mounts=mounts, swaps=swaps, name=new_install_name)
+            swaps = [d for d in new_devices if d.format.type == "swap"]
+            mounts = dict([(d.format.mountpoint, d) for d in new_devices
+                                if getattr(d.format, "mountpoint", None)])
+            new_root = Root(mounts=mounts, swaps=swaps, name=new_install_name)
             ui_roots.insert(0, new_root)
 
         # Add in all the existing (or autopart-created) operating systems.
@@ -374,12 +383,14 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
             page.pageTitle = root.name
 
             for device in root.swaps:
-                selector = page.addDevice("Swap", device.size, None, self.on_selector_clicked)
+                selector = page.addDevice("Swap",
+                                          Size(spec="%f MB" % device.size),
+                                          None, self.on_selector_clicked)
                 selector._device = device
                 selector._root = root
 
             for (mountpoint, device) in root.mounts.iteritems():
-                selector = page.addDevice(self._mountpointName(mountpoint) or device.fstype, device.size, mountpoint, self.on_selector_clicked)
+                selector = page.addDevice(self._mountpointName(mountpoint) or device.format.name, Size(spec="%f MB" % device.size), mountpoint, self.on_selector_clicked)
                 selector._device = device
                 selector._root = root
 
@@ -396,7 +407,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
             page.pageTitle = _("Unknown")
 
             for u in unused:
-                selector = page.addDevice(u.fstype, u.size, None, self.on_selector_clicked)
+                selector = page.addDevice(u.format.name, Size(spec="%f MB" % u.size), None, self.on_selector_clicked)
                 selector._device = u
                 selector._root = None
 
@@ -437,8 +448,8 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
 
         device = selector._device
 
-        if labelEntry.get_text():
-            device.label = labelEntry.get_text()
+        if labelEntry.get_text() and hasattr(device.format, "label"):
+            device.format.label = labelEntry.get_text()
 
     def _populate_right_side(self, selector):
         encryptCheckbox = self.builder.get_object("encryptCheckbox")
@@ -454,8 +465,8 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
         selectedDeviceLabel.set_text(selector.props.name)
         selectedDeviceDescLabel.set_text(self._description(selector.props.name))
 
-        labelEntry.set_text(device.label or "")
-        can_label = getattr(getFormat(device.fstype), "labelfsProg", "") != ""
+        labelEntry.set_text(getattr(device.format, "label", "") or "")
+        can_label = getattr(device.format, "labelfsProg", "") != ""
         labelEntry.set_sensitive(can_label)
 
         if labelEntry.get_sensitive():
@@ -463,9 +474,9 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
         else:
             labelEntry.set_tooltip_text(_("This file system does not support labels."))
 
-        sizeSpinner.set_range(device.min_size.convertTo(spec="mb"),
-                              device.max_size.convertTo(spec="mb"))
-        sizeSpinner.set_value(device.size.convertTo(spec="mb"))
+        sizeSpinner.set_range(device.minSize,
+                              device.maxSize)
+        sizeSpinner.set_value(device.size)
         sizeSpinner.set_sensitive(device.resizable)
 
         if sizeSpinner.get_sensitive():
@@ -476,19 +487,19 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
         encryptCheckbox.set_active(device.encrypted)
 
         # FIXME:  What do we do if we can't figure it out?
-        if device.type == "LVM Logical Volume":
+        if device.type == "lvmlv":
             typeCombo.set_active(1)
-        elif device.type == "Software RAID":
+        elif device.type == "mdarray":
             typeCombo.set_active(2)
-        elif device.type == "Partition":
+        elif device.type == "partition":
             typeCombo.set_active(3)
-        elif device.type.startswith("BTRFS"):
+        elif device.type.startswith("btrfs"):
             typeCombo.set_active(0)
 
         # FIXME:  What do we do if we can't figure it out?
         model = fsCombo.get_model()
         for i in range(0, len(model)):
-            if model[i][0] == device.fstype:
+            if model[i][0] == device.format.type:
                 fsCombo.set_active(i)
                 break
 
@@ -519,56 +530,76 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
         # create a device of the default type, using any disks, with an
         # appropriate fstype and mountpoint
         mountpoint = dialog.mountpoint
+        log.debug("requested size = %s  ; available space = %s"
+                    % (dialog.size, self._free_space))
         size = min(dialog.size, self._free_space)
-        fstype = self.storage.defaultFSType
+        fstype = self.storage.getFSType(mountpoint)
+        encrypted = self.data.autopart.encrypted
 
         # we're doing nothing here to ensure that bootable requests end up on
         # the boot disk, but the weight from platform should take care of this
 
         if mountpoint.lower() == "swap":
-            fstype = "swap"
             mountpoint = None
-        elif mountpoint == "/boot":
-            fstype = self.storage.defaultBootFSType
-        elif mountpoint == "/boot/efi":
-            if iutil.isMactel():
-                fstype = "hfs+"
-            else:
-                fstype = "efi"
-        elif not mountpoint or not mountpoint.startswith("/") or \
-             mountpoint in self.storage.mountpoints.keys():
-            return
 
-        if self.data.autopart.type == AUTOPART_TYPE_LVM and \
-             mountpoint != "/boot/efi":
-            device_class = UILogicalVolume
-        elif self.data.autopart.type == AUTOPART_TYPE_BTRFS and \
-             fstype != "swap" and \
-             not mountpoint.startswith("/boot"):
-            device_class = UIBTRFSSubVolume
+        # TODO: validate the mountpoint for sanity and uniqueness
+
+        device_type = self.data.autopart.type
+        if device_type == AUTOPART_TYPE_LVM and \
+             mountpoint == "/boot/efi":
+            device_type = AUTOPART_TYPE_PLAIN
+        elif device_type == AUTOPART_TYPE_BTRFS and \
+             fstype == "swap" or \
+             mountpoint and mountpoint.startswith("/boot"):
+            device_type = AUTOPART_TYPE_PLAIN
+
+        if device_type == AUTOPART_TYPE_LVM:
+            containers = [d for d in self._devices if d.type == "lvmvg"]
+        elif device_type == AUTOPART_TYPE_BTRFS:
+            containers = [d for d in self._devices if d.type == "btrfs volume"]
         else:
-            device_class = UIPartition
+            containers = None
 
-        device = device_class(mountpoint=mountpoint, size=size, fstype=fstype)
-        self._ui_devices.append(device)
-        self._free_space -= device.disk_space
+        disks = self._clearpartDevices
+        partitions = [d for d in self._devices if d.type == "partition"]
+
+        self.storage.newUIDevice(device_type,
+                                 size=float(size.convertTo(spec="mb")),
+                                 fstype=fstype,
+                                 mountpoint=mountpoint,
+                                 encrypted=encrypted,
+                                 disks=disks,
+                                 partitions=partitions,
+                                 containers=containers,
+                                 add_cb=self._add_cb,
+                                 remove_ext_cb=self._remove_cb,
+                                 isolate=True)
         self._do_refresh()
         self._updateSpaceDisplay()
 
     def _destroy_device(self, device):
-        self._ui_devices.remove(device)
+        self._devices.remove(device)
         self._deleted_devices.append(device)
 
-        # XXX This could be tricky. We don't want to count an LV's size twice
-        #     by counting it for the LV and again for the VG.
-        if not device.exists or device.type == "Partition":
-            log.debug("ui: destroy_device: space=%s  size=%s  free=%s" % (device.disk_space, device.size, self._free_space))
-            self._free_space += device.disk_space
+        if hasattr(device, "vg"):
+            device.vg._removeLogVol(device)
+        elif hasattr(device, "volume"):
+            device.volume._removeSubVolume(device.name)
+        elif getattr(device, "partedPartition", None) and \
+             device.disk is not None:
+            device.disk.format.removePartition(device.partedPartition)
+
+            # adjust all other PartitionDevice instances belonging to the
+            # same disk so the device name matches the potentially altered
+            # name of the parted.Partition
+            for _device in self._devices:
+                if getattr(_device, "disk", None) == device.disk:
+                    _device.updateName()
 
         # if this device has parents with no other children, remove them too
         for parent in device.parents:
-            all_parents = [p for d in self._ui_devices for p in d.parents]
-            if parent not in all_parents and parent.type != "Disk":
+            parent.kids -= 1
+            if parent.kids == 0 and not parent.isDisk:
                 self._destroy_device(parent)
 
     def _remove_from_root(self, root, device):
@@ -576,7 +607,8 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
             pass    # unused device
         elif device in root.swaps:
             root.swaps.remove(device)
-        elif device.mountpoint and device in root.mounts.values():
+        elif getattr(device.format, "mountpoint", None) and \
+             device in root.mounts.values():
             mountpoints = [m for (m,d) in root.mounts.items() if d == device]
             for mountpoint in mountpoints:
                 root.mounts.pop(mountpoint)
@@ -599,6 +631,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
         self._updateSpaceDisplay()
 
     def on_remove_clicked(self, button):
+        # FIXME: removing a btrfs subvol doesn't cause its size to be reduced
         if self._current_selector:
             device = self._current_selector._device
             if device.exists:
@@ -607,7 +640,8 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
                 # schedule actions to delete the thing.
                 dialog = ConfirmDeleteDialog(self.data)
                 with enlightbox(self.window, dialog.window):
-                    dialog.refresh(device.mountpoint, device.name)
+                    dialog.refresh(getattr(device.format, "mountpoint", ""),
+                                   device.name)
                     rc = dialog.run()
 
                     if rc == 0:
@@ -646,11 +680,11 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
             self._update_ui_for_removals()
 
     def on_summary_clicked(self, button):
-        free = self.storage.getFreeSpace()
+        free = self._free_space
         dialog = SelectedDisksDialog(self.data)
 
         with enlightbox(self.window, dialog.window):
-            dialog.refresh(self._clearpartDevices(), free, showRemove=False)
+            dialog.refresh(self._clearpartDevices, free, showRemove=False)
             dialog.run()
 
     def on_configure_clicked(self, button):
@@ -684,75 +718,27 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
 
         self._removeButton.set_sensitive(True)
 
-    def _do_autopart(self):
-        # Approach:
-        #
-        #   NOTE: There shouldn't be any non-existent partitions in play at this
-        #         point.
-        #
-        #   XXX this estimate is going to suck for everything except lvm since
-        #       the accuracy is reduced for each additional partition request
-        #
-        #       This is true for all the estimates in this spoke. If we want to
-        #       improve this we'll need to do some approximation of partition
-        #       allocation instead of assuming all free space will get used.
-        #
-        #   - use DiskChunk and VGChunk to get a decent estimate
-        #       - use DiskChunk to establish partition/container size
-        #       - use VGChunk to establish lv size based on container size
-        #         obtained above
-        #
-        chunk_size = int(self._free_space.convertTo(spec="mb"))
-        chunk_reqs = []
-        for request in self.storage.autoPartitionRequests:
-            if request.requiredSpace and \
-               request.requiredSpace > self._free_space.convertTo(spec="mb"):
-                continue
-
-            # figure out the device type for this request
-            if request.lv and self.data.autopart.type == AUTOPART_TYPE_LVM:
-                device_class = UILogicalVolume
-            elif request.btr and self.data.autopart.type == AUTOPART_TYPE_BTRFS:
-                device_class = UIBTRFSSubVolume
-            else:
-                device_class = UIPartition
-
-            if request.fstype is None:
-                request.fstype = self.storage.getFSType(mountpoint=request.mountpoint)
-            elif request.fstype in ("prepboot", "efi", "biosboot", "hfs+") and \
-                 request.fstype in [d.fstype for d in self._ui_devices]:
-                # XXX We could use the code directly from storage.partitioning
-                #     since this function can only be called when no new devices
-                #     have been requested.
-                #
-                # there should never be a need for more than one of these
-                # partitions, so skip them.
-                continue
-
-            new_device = device_class(size=Size(spec="%fmb" % request.size),
-                                      fstype=request.fstype,
-                                      mountpoint=request.mountpoint)
-            self._ui_devices.append(new_device)
-            if request.grow:
-                req = Request(new_device)
-                req.base = request.size
-                if request.maxSize:
-                    req.max_growth = request.maxSize - request.size
-
-                chunk_reqs.append(req)
-            else:
-                chunk_size -= request.size
+    def _add_cb(self, device):
+        log.debug("adding device %s (%d) to device list" % (device.name, device.id))
+        self._devices.append(device)
 
-            self._free_space -= new_device.disk_space
+    def _remove_cb(self, device):
+        if device in self._devices:
+            log.debug("removing device %s (%d) from device list" % (device.name, device.id))
+            self._devices.remove(device)
 
-        chunk = Chunk(chunk_size, chunk_reqs)
-        chunk.growRequests()
-
-        # now grow the requests by the amounts we've calculated above
-        for req in chunk.requests:
-            growth_mb = Size(spec="%f mb" % chunk.lengthToSize(req.growth))
-            req.device.size += growth_mb
-            self._free_space -= growth_mb
+    def _do_autopart(self):
+        # There are never any non-existent devices around when this runs.
+        disks = self._clearpartDevices
+        partitions = [d for d in self._devices if d.type == "partition"]
+        vgs = [d for d in self._devices if d.type == "lvmvg"]
+
+        log.debug("running automatic partitioning")
+        doAutoPartition(self.storage, self.data,
+                        disks=disks, partitions=partitions, vgs=vgs,
+                        add_cb=self._add_cb, remove_ext_cb=self._remove_cb,
+                        isolate=True)
+        log.debug("finished automatic partitioning")
 
     def on_create_clicked(self, button):
         # Then do autopartitioning.  We do not do any clearpart first.  This is
@@ -760,8 +746,12 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
         self._do_autopart()
 
         # Refresh the spoke to make the new partitions appear.
+        log.debug("refreshing ui")
         self._do_refresh()
+        log.debug("finished refreshing ui")
+        log.debug("updating space display")
         self._updateSpaceDisplay()
+        log.debug("finished updating space display")
         self._accordion.expandPage(new_install_name)
 
         # And then display the first filesystem on the RHS.
@@ -782,203 +772,4 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
         elif text == _("Standard Partition"):
             self._optionsNotebook.hide()
 
-class UIDevice(object):
-    type = "Generic Device"
-    raid_levels = []
-    create_type = None
-
-    def __init__(self, size=None, mountpoint=None, fstype=None, name=None,
-                 raid_level=None, parents=None, exists=False, encrypted=False,
-                 label=None, resizable=False, min_size=None, max_size=None,
-                 device_id=None):
-        self.size = size
-        self.mountpoint = mountpoint
-        self.fstype = fstype
-        self.name = name
-        self.raid_level = raid_level
-        self.parents = parents or []
-        self.exists = exists
-        self.encrypted = encrypted
-        self.label = label
-
-        self.resizable = resizable
-        self.min_size = min_size
-        self.max_size = max_size
-
-        self.device_id = device_id
-
-        if self.min_size is None:
-            self.min_size = self.size
-
-        if self.max_size is None:
-            self.max_size = self.size
-
-    @property
-    def disk_space(self):
-        space = self.size
-        if self.raid_level in ("raid1", "raid10"):
-            space = self.size * len(self.parents)
-        elif self.raid_level == "raid5":
-            space = self.size * (float(len(parents)) / (len(parents) - 1))
-        elif self.raid_level == "raid6":
-            space = self.size * (float(len(parents)) / (len(parents) - 2))
-
-        return space
-
-    #
-    # stuff so we can use these with storage.partitioning.Request
-    #
-    @property
-    def req_grow(self):
-        return True
-
-    @property
-    def id(self):
-        return 26
-
-class UIDisk(UIDevice):
-    type = "Disk"
-
-class UIPartition(UIDevice):
-    type = "Partition"
-    create_type = AUTOPART_TYPE_PLAIN
-
-class UIVolumeGroup(UIDevice):
-    type = "LVM Volume Group"
-
-class UILogicalVolume(UIDevice):
-    type = "LVM Logical Volume"
-    raid_levels = ["linear", "raid0", "raid1", "raid10"]
-    create_type = AUTOPART_TYPE_LVM
-
-class UIRAIDArray(UIDevice):
-    type = "Software RAID"
-    raid_levels = ["raid0", "raid1", "raid5", "raid10"]
-
-class UIBTRFSVolume(UIDevice):
-    type = "BTRFS"
-    raid_levels = ["linear", "raid0", "raid1", "raid10"]
-    create_type = AUTOPART_TYPE_BTRFS
-
-class UIBTRFSSubVolume(UIDevice):
-    type = "BTRFS Sub-Volume"
-    create_type = AUTOPART_TYPE_BTRFS
-
-"""
-    UI for device editing
-
-        - convert current StorageDevice tree to UIDevice tree
-        - modify UIDevice tree in UI
-        - generate a list of actions to represent the changes
-"""
-def get_ui_device_class(device):
-    ui_class = None
-    if device.isDisk:
-        ui_class = UIDisk
-    elif device.type == "partition":
-        ui_class = UIPartition
-    elif device.type == "mdarray":
-        ui_class = UIRAIDArray
-    elif device.type == "lvmvg":
-        ui_class = UIVolumeGroup
-    elif device.type == "lvmlv":
-        ui_class = UILogicalVolume
-    elif device.type == "luks/dm-crypt":
-        ui_class = get_ui_device_type(device.parents[0])
-
-    log.debug("ui: get_ui_device_class returning %s for device %s"
-              % (ui_class, device))
-    return ui_class
-
-def get_ui_device_raid_level(device):
-    level = None
-    if device.type == "mdarray":
-        if device.level == mdraid.RAID0:
-            level = "raid0"
-        elif device.level == mdraid.RAID1:
-            level = "raid1"
-        elif device.level == mdraid.RAID5:
-            level = "raid5"
-        elif device.level == mdraid.RAID6:
-            level = "raid6"
-        elif device.level == mdraid.RAID10:
-            level = "raid10"
-    elif device.type == "lvmlv":
-        # TODO: implement striping and mirroring in LVMLogicalVolumeDevice
-        pass
-    elif device.type == "btrfs volume":
-        level = device.dataLevel
-
-    return level
-
-def get_ui_parents(storage_device, ui_devices):
-    ui_parents = []
-    for parent in storage_device.parents:
-        for u in ui_devices:
-            if u.name == parent.name:
-                ui_parents.append(u)
-
-    return ui_parents
-
-def get_ui_devices(storage):
-    devices = storage.devicetree.devices[:]
-    ui_devices = []
-    while devices:
-        roots = [d for d in devices if
-                        not [p for p in d.parents if p in devices]]
-        for device in roots:
-            ui_class = get_ui_device_class(device)
-            devices.remove(device)
-            if ui_class is None:
-                continue
-
-            min_size = device.size
-            max_size = device.size
-            resizable = False
-            if device.format.type == "luks" and \
-               storage.devicetree.getChildren(device):
-                continue
-            elif device.type == "luks/dm-crypt":
-                encrypted = True
-                raid_level = get_ui_device_raid_level(device.parents[0])
-                exists = device.parents[0].exists
-                devices.remove(device.parents[0])
-                parents = get_ui_parents(device.parents[0], ui_devices)
-            else:
-                encrypted = False
-                raid_level = get_ui_device_raid_level(device)
-                exists = device.exists
-                parents = get_ui_parents(device, ui_devices)
-                if device.resizable:
-                    resizable = True
-                    min_size = device.minSize
-                    max_size = device.maxSize
-
-            ui_device = ui_class(name=device.name,
-                                 size=Size(spec="%f mb" % device.size),
-                                 fstype=device.format.type,
-                                 mountpoint=getattr(device.format,
-                                                    "mountpoint", None),
-                                 raid_level=raid_level,
-                                 encrypted=encrypted,
-                                 label=getattr(device.format, "label", None),
-                                 parents=parents,
-                                 exists=exists,
-                                 resizable=resizable,
-                                 min_size=Size(spec="%f mb" % min_size),
-                                 max_size=Size(spec="%f mb" % max_size),
-                                 device_id=device.id)
-            ui_devices.append(ui_device)
-
-    return ui_devices
-
-class UIRoot(object):
-    def __init__(self, name=None, mounts=None, swaps=None):
-        self.name = name
-        self.mounts = mounts or {}
-        self.swaps = swaps or []
-
-    @property
-    def devices(self):
-        return self.mounts.values() + self.swaps[:]
 
diff --git a/pyanaconda/ui/gui/spokes/storage.py b/pyanaconda/ui/gui/spokes/storage.py
index 6e5df44..104212a 100644
--- a/pyanaconda/ui/gui/spokes/storage.py
+++ b/pyanaconda/ui/gui/spokes/storage.py
@@ -292,7 +292,7 @@ class StorageSpoke(NormalSpoke, StorageChecker):
         self.data.autopart.autopart = self.autopart
 
         # no thanks, lvm
-        self.data.autopart.type = AUTOPART_TYPE_PLAIN
+        self.data.autopart.type = AUTOPART_TYPE_BTRFS
 
         if self.autopart:
             self.clearPartType = CLEARPART_TYPE_ALL
