diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/backend.py anaconda-storage/backend.py
--- anaconda/backend.py	2009-02-13 23:03:10.000000000 -0600
+++ anaconda-storage/backend.py	2009-02-15 13:50:08.000000000 -0600
@@ -156,8 +156,9 @@
         if not anaconda.mediaDevice or not os.path.exists(installimg):
             return
 
+        free = anaconda.id.storage.fsset.fsFreeSpace(anaconda.rootPath)
         self._loopbackFile = "%s%s/rhinstall-install.img" % (anaconda.rootPath,
-                             anaconda.id.fsset.filesystemSpace(anaconda.rootPath)[0][0])
+                                                             free[0][0])
 
         try:
             win = anaconda.intf.waitWindow(_("Copying File"),
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/bootloader.py anaconda-storage/bootloader.py
--- anaconda/bootloader.py	2009-02-13 23:03:10.000000000 -0600
+++ anaconda-storage/bootloader.py	2009-02-17 12:41:56.000000000 -0600
@@ -60,49 +60,36 @@
         anaconda.id.bootloader.updateDriveList(pref)
 
     if iutil.isEfi() and not anaconda.id.bootloader.device:
-        drives = anaconda.id.diskset.disks.keys()
-        drives.sort()
         bootPart = None
-        for drive in drives:
-            disk = anaconda.id.diskset.disks[drive]
-            for part in disk.partitions:
-                if part.active and partedUtils.isEfiSystemPartition(part):
-                    bootPart = part.getDeviceNodeName()
+        partitions = anaconda.id.storage.partitions
+        for part in partitions:
+            if part.partedPartition.active and \
+               partedUtils.isEfiSystemPartition(part.partedPartition):
+                    bootPart = part.name
                     break
-            if bootPart:
-                break
         if bootPart:
             anaconda.id.bootloader.setDevice(bootPart)
-            dev = Device()
-            dev.device = bootPart
-            anaconda.id.fsset.add(FileSystemSetEntry(dev, None, fileSystemTypeGet("efi")))
 
 # iSeries bootloader on upgrades
     if iutil.getPPCMachine() == "iSeries" and not anaconda.id.bootloader.device:
-        drives = anaconda.id.diskset.disks.keys()
-        drives.sort()
         bootPart = None
-        for drive in drives:
-            disk = anaconda.id.diskset.disks[drive]
-            for part in disk.partitions:
-                if part.active and part.getFlag(parted.PARTITION_PREP):
-                    bootPart = part.getDeviceNodeName()
-                    break
-            if bootPart:
+        partitions = anaconda.id.storage.partitions
+        for part in partitions:
+            if part.partedPartition.active and \
+               part.partedPartition.getFlag(parted.PARTITION_PREP):
+                bootPart = part.name
                 break
         if bootPart:
             anaconda.id.bootloader.setDevice(bootPart)
-            dev = Device()
-            dev.device = bootPart
-            anaconda.id.fsset.add(FileSystemSetEntry(dev, None, fileSystemTypeGet("PPC PReP Boot")))
 
-    choices = anaconda.id.fsset.bootloaderChoices(anaconda.id.diskset, anaconda.id.bootloader)
+    choices = anaconda.id.storage.fsset.bootloaderChoices(anaconda.id.bootloader)
     if not choices and iutil.getPPCMachine() != "iSeries":
 	anaconda.dispatch.skipStep("instbootloader")
     else:
 	anaconda.dispatch.skipStep("instbootloader", skip = 0)
 
-    anaconda.id.bootloader.images.setup(anaconda.id.diskset, anaconda.id.fsset)
+    # FIXME: ...
+    anaconda.id.bootloader.images.setup(anaconda.id.storage)
 
     if anaconda.id.bootloader.defaultDevice != None and choices:
         keys = choices.keys()
@@ -150,11 +137,7 @@
 
     kernelList = []
     otherList = []
-    root = anaconda.id.fsset.getEntryByMountPoint('/')
-    if root:
-        rootDev = root.device.getDevice()
-    else:
-        rootDev = None
+    rootDev = getattr(anaconda.id.rootDevice, "path", None)
     defaultDev = anaconda.id.bootloader.images.getDefault()
 
     kernelLabel = None
@@ -204,8 +187,10 @@
 
     dosync()
     try:
-        anaconda.id.bootloader.write(anaconda.rootPath, anaconda.id.fsset, anaconda.id.bootloader,
-                                     anaconda.id.instLanguage, kernelList, otherList, defaultDev,
+        anaconda.id.bootloader.write(anaconda.rootPath, anaconda.id.storage,
+                                     anaconda.id.bootloader,
+                                     anaconda.id.instLanguage,
+                                     kernelList, otherList, defaultDev,
                                      justConfigFile, anaconda.intf)
 	if not justConfigFile:
 	    w.pop()
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/cmdline.py anaconda-storage/cmdline.py
--- anaconda/cmdline.py	2009-02-13 23:03:10.000000000 -0600
+++ anaconda-storage/cmdline.py	2009-02-15 14:45:19.000000000 -0600
@@ -114,10 +114,6 @@
         pass
 
     def run(self, anaconda):
-        anaconda.id.fsset.registerMessageWindow(self.messageWindow)
-        anaconda.id.fsset.registerProgressWindow(self.progressWindow)
-        anaconda.id.fsset.registerWaitWindow(self.waitWindow)        
-
         (step, instance) = anaconda.dispatch.currentStep()
         while step:
             if stepToClasses.has_key(step):
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/fsset.py anaconda-storage/fsset.py
--- anaconda/fsset.py	2009-02-13 23:31:37.000000000 -0600
+++ anaconda-storage/fsset.py	1969-12-31 18:00:00.000000000 -0600
@@ -1,3145 +0,0 @@
-#
-# fsset.py: filesystem management
-#
-# Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007  Red Hat, Inc.
-# All rights reserved.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-#
-# Author(s): Matt Wilson <msw@redhat.com>
-#            Jeremy Katz <katzj@redhat.com>
-#
-
-import math
-import string
-import isys
-import iutil
-import os
-import resource
-import posix
-import stat
-import errno
-import parted
-import sys
-import struct
-import partitions
-import partedUtils
-import raid
-import lvm
-import time
-import types
-from flags import flags
-from constants import *
-
-import gettext
-_ = lambda x: gettext.ldgettext("anaconda", x)
-
-import logging
-log = logging.getLogger("anaconda")
-
-class SuspendError(Exception):
-    pass
-
-class OldSwapError(Exception):
-    pass
-
-class ResizeError(Exception):
-    pass
-
-defaultMountPoints = ['/', '/boot', '/home', '/tmp', '/usr', '/var', '/usr/local', '/opt']
-
-if iutil.isS390():
-    # Many s390 have 2G DASDs, we recomment putting /usr/share on its own DASD
-    defaultMountPoints.insert(5, '/usr/share')
-
-if iutil.isEfi():
-    defaultMountPoints.insert(2, '/boot/efi')
-
-fileSystemTypes = {}
-
-def fileSystemTypeGetDefault():
-    if fileSystemTypeGet('ext4').isSupported():
-        return fileSystemTypeGet('ext4')
-    if fileSystemTypeGet('ext3').isSupported():
-        return fileSystemTypeGet('ext3')
-    elif fileSystemTypeGet('ext2').isSupported():
-        return fileSystemTypeGet('ext2')
-    else:
-        raise ValueError, "None of ext4, ext3 or ext2 is supported in your kernel!"
-
-def fileSystemTypeGetDefaultBoot():
-    if fileSystemTypeGet('ext3').isSupported():
-        return fileSystemTypeGet('ext3')
-    elif fileSystemTypeGet('ext2').isSupported():
-        return fileSystemTypeGet('ext2')
-    else:
-        raise ValueError, "You have neither ext3 or ext2 support in your kernel!"
-
-def fileSystemTypeGet(key):
-    if fileSystemTypes.has_key(key):
-        return fileSystemTypes[key]
-    else:
-        return fileSystemTypeGetDefault()
-
-def fileSystemTypeRegister(klass):
-    fileSystemTypes[klass.getName()] = klass
-
-def fileSystemTypeGetTypes():
-    return fileSystemTypes.copy()
-
-def getUsableLinuxFs():
-    rc = []
-    for fsType in fileSystemTypes.keys():
-        if fileSystemTypes[fsType].isMountable() and \
-               fileSystemTypes[fsType].isLinuxNativeFS():
-            rc.append(fsType)
-
-    # make sure the default is first in the list, kind of ugly
-    default = fileSystemTypeGetDefault()
-    defaultName = default.getName()
-    if defaultName in rc:
-        del rc[rc.index(defaultName)]
-        rc.insert(0, defaultName)
-    return rc
-
-def devify(device):
-    if device in ["proc", "devpts", "sysfs", "tmpfs"] or device.find(":") != -1:
-        return device
-    elif device == "sys":
-        return "sysfs"
-    elif device == "shm":
-        return "tmpfs"
-    elif device == "spufs":
-        return "spufs"
-    elif device != "none" and device[0] != '/':
-        return "/dev/" + device
-    else:
-        return device
-
-class FileSystemType:
-    kernelFilesystems = {}
-    lostAndFoundContext = None
-
-    def __init__(self):
-        self.deviceArguments = {}
-        self.formattable = 0
-        self.checked = 0
-        self.name = ""
-        self.linuxnativefs = 0
-        self.fusefs = 0
-        self.partedFileSystemType = None
-        self.partedPartitionFlags = []
-        self.maxSizeMB = 8 * 1024 * 1024
-        self.supported = -1
-        self.defaultOptions = "defaults"
-        self.migratetofs = None
-        self.extraFormatArgs = []
-        self.maxLabelChars = 16
-        self.packages = []
-        self.needProgram = []
-        self.resizable = False
-        self.supportsFsProfiles = False
-        self.fsProfileSpecifier = None
-        self.fsprofile = None
-        self.bootable = False
-
-    def createLabel(self, mountpoint, maxLabelChars, kslabel = None):
-        # If a label was specified in the kickstart file, return that as the
-        # label.
-        if kslabel:
-            return kslabel
-
-        if len(mountpoint) > maxLabelChars:
-            return mountpoint[0:maxLabelChars]
-        else:
-            return mountpoint
-
-    def isBootable(self):
-        return self.bootable
-
-    def isResizable(self):
-        return self.resizable
-    def resize(self, entry, size, progress, chroot='/'):
-        pass
-    def getMinimumSize(self, device):
-        log.warning("Unable to determinine minimal size for %s", device)
-        return 1
-
-    def isKernelFS(self):
-        """Returns True if this is an in-kernel pseudo-filesystem."""
-        return False
-
-    def mount(self, device, mountpoint, readOnly=0, bindMount=0,
-              instroot=""):
-        if not self.isMountable():
-            return
-        iutil.mkdirChain("%s/%s" %(instroot, mountpoint))
-        if flags.selinux:
-            ret = isys.resetFileContext(mountpoint, instroot)
-            log.info("set SELinux context for mountpoint %s to %s" %(mountpoint, ret))
-        log.debug("mounting %s on %s/%s as %s" %(device, instroot, 
-                                                 mountpoint, self.getMountName()))
-        isys.mount(device, "%s/%s" %(instroot, mountpoint),
-                   fstype = self.getMountName(), 
-                   readOnly = readOnly, bindMount = bindMount,
-                   options = self.defaultOptions)
-
-        if flags.selinux:
-            ret = isys.resetFileContext(mountpoint, instroot)
-            log.info("set SELinux context for newly mounted filesystem root at %s to %s" %(mountpoint, ret))
-            if FileSystemType.lostAndFoundContext is None:
-                FileSystemType.lostAndFoundContext = \
-                    isys.matchPathContext("/lost+found")
-            isys.setFileContext("%s/lost+found" % (mountpoint,),
-                FileSystemType.lostAndFoundContext, instroot)
-
-    def umount(self, device, path):
-        isys.umount(path, removeDir = 0)
-
-    def getName(self, quoted = 0):
-        """Return the name of the filesystem.  Set quoted to 1 if this
-        should be quoted (ie, it's not for display)."""
-        if quoted:
-            if self.name.find(" ") != -1:
-                return "\"%s\"" %(self.name,)
-        return self.name
-
-    def getMountName(self, quoted = 0):
-        return self.getName(quoted)
-
-    def getNeededPackages(self):
-        return self.packages
-
-    def registerDeviceArgumentFunction(self, klass, function):
-        self.deviceArguments[klass] = function
-
-    def formatDevice(self, entry, progress, chroot='/'):
-        if self.isFormattable():
-            raise RuntimeError, "formatDevice method not defined"
-
-    def migrateFileSystem(self, device, message, chroot='/'):
-        if self.isMigratable():
-            raise RuntimeError, "migrateFileSystem method not defined"
-
-    def labelDevice(self, entry, chroot):
-        pass
-
-    def clobberDevice(self, entry, chroot):
-        pass
-
-    def isFormattable(self):
-        return self.formattable
-
-    def isLinuxNativeFS(self):
-        return self.linuxnativefs
-
-    def setFsProfile(self, fsprofile=None):
-        if not self.supportsFsProfiles:
-            raise RuntimeError, "%s does not support profiles" % (self,)
-        self.fsprofile = fsprofile
-
-    def getFsProfileArgs(self):
-        if not self.supportsFsProfiles:
-            raise RuntimeError, "%s does not support profiles" % (self,)
-        args = None 
-        if self.fsprofile:
-            args = []
-            if self.fsProfileSpecifier:
-                args.extend(self.fsProfileSpecifier)
-            args.extend(self.fsprofile)
-        return args
-
-    def readProcFilesystems(self):
-        f = open("/proc/filesystems", 'r')
-        if not f:
-            pass
-        lines = f.readlines()
-        for line in lines:
-            fields = string.split(line)
-            if fields[0] == "nodev":
-                fsystem = fields[1]
-            else:
-                fsystem = fields[0]
-            FileSystemType.kernelFilesystems[fsystem] = None
-
-    def isMountable(self):
-        if not FileSystemType.kernelFilesystems:
-            self.readProcFilesystems()
-        if self.fusefs:
-            return FileSystemType.kernelFilesystems.has_key("fuse")
-
-        return FileSystemType.kernelFilesystems.has_key(self.getMountName()) or self.getName() == "auto"
-
-    def isSupported(self):
-        # check to ensure we have the binaries they need
-        for p in self.needProgram:
-            if len(filter(lambda d: os.path.exists("%s/%s" %(d, p)),
-                          os.environ["PATH"].split(":"))) == 0:
-                return False
-
-        if self.supported == -1:
-            return self.isMountable()
-        return self.supported
-
-    def isChecked(self):
-        return self.checked
-
-    def getDeviceArgs(self, device):
-        deviceArgsFunction = self.deviceArguments.get(device.__class__)
-        if not deviceArgsFunction:
-            return []
-        return deviceArgsFunction(device)
-
-    def getPartedFileSystemType(self):
-        return self.partedFileSystemType
-
-    def getPartedPartitionFlags(self):
-        return self.partedPartitionFlags
-
-    # note that this returns the maximum size of a filesystem in megabytes
-    def getMaxSizeMB(self):
-        return self.maxSizeMB
-
-    def getDefaultOptions(self, mountpoint):
-        return self.defaultOptions
-
-    def getMigratableFSTargets(self):
-        retval = []
-        if not self.migratetofs:
-            return retval
-
-        for fs in self.migratetofs:
-            if fileSystemTypeGet(fs).isSupported():
-                retval.append(fs)
-
-        return retval
-
-    def isMigratable(self):
-        if len(self.getMigratableFSTargets()) > 0:
-            return 1
-        else:
-            return 0
-
-
-class reiserfsFileSystem(FileSystemType):
-    def __init__(self):
-        FileSystemType.__init__(self)
-        self.partedFileSystemType = parted.fileSystemType["reiserfs"]
-        self.formattable = 1
-        self.checked = 1
-        self.linuxnativefs = 1
-        self.bootable = True
-        # this is totally, 100% unsupported.  Boot with "linux reiserfs"
-        # at the boot: prompt will let you make new reiserfs filesystems
-        # in the installer.  Bugs filed when you use this will be closed
-        # WONTFIX.
-        if flags.cmdline.has_key("reiserfs"):
-            self.supported = -1
-        else:
-            self.supported = 0
-
-        self.name = "reiserfs"
-        self.packages = [ "reiserfs-utils" ]
-        self.needProgram = [ "mkreiserfs", "reiserfstune" ]
-
-        self.maxSizeMB = 8 * 1024 * 1024
-
-    def formatDevice(self, entry, progress, chroot='/'):
-        devicePath = entry.device.setupDevice(chroot)
-
-        p = os.pipe()
-        os.write(p[1], "y\n")
-        os.close(p[1])
-
-        rc = iutil.execWithRedirect("mkreiserfs",
-                                    [devicePath],
-                                    stdin = p[0],
-                                    stdout = "/dev/tty5",
-                                    stderr = "/dev/tty5", searchPath = 1)
-
-        if rc:
-            raise SystemError
-
-    def labelDevice(self, entry, chroot):
-        devicePath = entry.device.setupDevice(chroot)
-        label = self.createLabel(entry.mountpoint, self.maxLabelChars,
-                                 kslabel = entry.label)
-        rc = iutil.execWithRedirect("reiserfstune",
-                                    ["--label", label, devicePath],
-                                    stdout = "/dev/tty5",
-                                    stderr = "/dev/tty5", searchPath = 1)
-        if rc:
-            raise SystemError
-        entry.setLabel(label)
-
-fileSystemTypeRegister(reiserfsFileSystem())
-
-class xfsFileSystem(FileSystemType):
-    def __init__(self):
-        FileSystemType.__init__(self)
-        self.partedFileSystemType = parted.fileSystemType["xfs"]
-        self.formattable = 1
-        self.checked = 1
-        self.linuxnativefs = 1
-        self.name = "xfs"
-        self.maxSizeMB = 16 * 1024 * 1024
-        self.maxLabelChars = 12
-        self.supported = -1
-        if not os.path.exists("/sbin/mkfs.xfs") and not os.path.exists("/usr/sbin/mkfs.xfs") and not os.path.exists("/usr/sbin/xfs_admin"):
-            self.supported = 0
-
-        self.packages = [ "xfsprogs" ]
-        self.needProgram = [ "mkfs.xfs", "xfs_admin" ]
-
-    def formatDevice(self, entry, progress, chroot='/'):
-        devicePath = entry.device.setupDevice(chroot)
-
-        rc = iutil.execWithRedirect("mkfs.xfs", ["-f", devicePath],
-                                    stdout = "/dev/tty5",
-                                    stderr = "/dev/tty5", searchPath = 1)
-
-        if rc:
-            raise SystemError
-
-    def labelDevice(self, entry, chroot):
-        devicePath = entry.device.setupDevice(chroot)
-        label = self.createLabel(entry.mountpoint, self.maxLabelChars,
-                                 kslabel = entry.label)
-        rc = iutil.execWithRedirect("xfs_admin",
-                                    ["-L", label, devicePath],
-                                    stdout = "/dev/tty5",
-                                    stderr = "/dev/tty5", searchPath = 1)
-        if rc:
-            raise SystemError
-        entry.setLabel(label)
-
-fileSystemTypeRegister(xfsFileSystem())
-
-class jfsFileSystem(FileSystemType):
-    def __init__(self):
-        FileSystemType.__init__(self)
-        self.partedFileSystemType = parted.fileSystemType["jfs"]
-        self.formattable = 1
-        self.checked = 1
-        self.linuxnativefs = 1
-        self.maxLabelChars = 16
-        self.bootable = True
-        # this is totally, 100% unsupported.  Boot with "linux jfs"
-        # at the boot: prompt will let you make new reiserfs filesystems
-        # in the installer.  Bugs filed when you use this will be closed
-        # WONTFIX.
-        if flags.cmdline.has_key("jfs"):
-            self.supported = -1
-        else:
-            self.supported = 0
-
-        self.name = "jfs"
-        self.packages = [ "jfsutils" ]
-        self.needProgram = [ "mkfs.jfs", "jfs_tune" ]
-
-        self.maxSizeMB = 8 * 1024 * 1024
-
-    def labelDevice(self, entry, chroot):
-        devicePath = entry.device.setupDevice(chroot)
-        label = self.createLabel(entry.mountpoint, self.maxLabelChars,
-                                 kslabel = entry.label)
-        rc = iutil.execWithRedirect("jfs_tune",
-                                   ["-L", label, devicePath],
-                                    stdout = "/dev/tty5",
-                                    stderr = "/dev/tty5", searchPath = 1)
-        if rc:
-            raise SystemError
-        entry.setLabel(label)
-
-    def formatDevice(self, entry, progress, chroot='/'):
-        devicePath = entry.device.setupDevice(chroot)
-
-        rc = iutil.execWithRedirect("mkfs.jfs",
-                                    ["-q", devicePath],
-                                    stdout = "/dev/tty5",
-                                    stderr = "/dev/tty5", searchPath = 1)
-
-        if rc:
-            raise SystemError
-
-fileSystemTypeRegister(jfsFileSystem())
-
-class gfs2FileSystem(FileSystemType):
-    def __init__(self):
-        FileSystemType.__init__(self)
-        self.partedFileSystemType = None
-        self.formattable = 1
-        self.checked = 1
-        self.linuxnativefs = 1
-        if flags.cmdline.has_key("gfs2"):
-            self.supported = -1
-        else:
-            self.supported = 0
-
-        self.name = "gfs2"
-        self.packages = [ "gfs2-utils" ]
-        self.needProgram = [ "mkfs.gfs2" ]
-
-        self.maxSizeMB = 8 * 1024 * 1024
-
-    def formatDevice(self, entry, progress, chroot='/'):
-        devicePath = entry.device.setupDevice(chroot)
-        rc = iutil.execWithRedirect("mkfs.gfs2",
-                                    ["-j", "1", "-p", "lock_nolock",
-                                     "-O", devicePath],
-                                    stdout = "/dev/tty5",
-                                    stderr = "/dev/tty5", searchPath = 1)
-
-        if rc:
-            raise SystemError
-
-fileSystemTypeRegister(gfs2FileSystem())
-
-class extFileSystem(FileSystemType):
-    def __init__(self):
-        FileSystemType.__init__(self)
-        self.partedFileSystemType = None
-        self.formattable = 1
-        self.checked = 1
-        self.linuxnativefs = 1
-        self.maxSizeMB = 8 * 1024 * 1024
-        self.packages = [ "e2fsprogs" ]
-        self.supportsFsProfiles = True
-        self.fsProfileSpecifier = "-T"
-        self.resizable = True
-        self.bootable = True
-
-    def resize(self, entry, size, progress, chroot='/'):
-        devicePath = entry.device.setupDevice(chroot)
-
-        log.info("checking %s prior to resize" %(devicePath,))
-        w = None
-        if progress:
-            w = progress(_("Checking"),
-                         _("Checking filesystem on %s...") %(devicePath),
-                         100, pulse = True)
-
-        rc = iutil.execWithPulseProgress("e2fsck", ["-f", "-p", "-C", "0", devicePath],
-                                         stdout="/tmp/resize.out",
-                                         stderr="/tmp/resize.out",
-                                         progress = w)
-        if rc >= 4:
-            raise ResizeError, ("Check of %s failed: %s" %(devicePath, rc), devicePath)
-        if progress:
-            w.pop()
-            w = progress(_("Resizing"),
-                         _("Resizing filesystem on %s...") %(devicePath),
-                         100, pulse = True)
-
-        log.info("resizing %s" %(devicePath,))
-        rc = iutil.execWithPulseProgress("resize2fs",
-                                         ["-p", devicePath, "%sM" %(size,)],
-                                         stdout="/tmp/resize.out",
-                                         stderr="/tmp/resize.out",
-                                         progress = w)
-        if progress:
-            w.pop()
-        if rc:
-            raise ResizeError, ("Resize of %s failed: %s" %(devicePath, rc), devicePath)
-
-    def getMinimumSize(self, device):
-        """Return the minimum filesystem size in megabytes"""
-        devicePath = "/dev/%s" % (device,)
-
-        # FIXME: it'd be nice if we didn't have to parse this out ourselves
-        buf = iutil.execWithCapture("dumpe2fs",
-                                    ["-h", devicePath],
-                                    stderr = "/dev/tty5")
-        blocks = free = bs = 0
-        for l in buf.split("\n"):
-            if l.startswith("Free blocks"):
-                try:
-                    free = l.split()[2]
-                    free = int(free)
-                except Exception, e:
-                    log.warning("error determining free blocks on %s: %s" %(devicePath, e))
-                    free = 0
-            elif l.startswith("Block size"):
-                try:
-                    bs = l.split()[2]
-                    bs = int(bs)
-                except Exception, e:
-                    log.warning("error determining block size of %s: %s" %(devicePath, e))
-                    bs = 0
-            elif l.startswith("Block count"):
-                try:
-                    blocks = l.split()[2]
-                    blocks = int(blocks)
-                except Exception, e:
-                    log.warning("error determining block count of %s: %s" %(devicePath, e))
-                    blocks = 0
-
-        if free == 0 or bs == 0:
-            log.warning("Unable to determinine minimal size for %s", devicePath)
-            return 1
-
-        used = math.ceil((blocks - free) * bs / 1024.0 / 1024.0)
-        log.info("used size of %s is %s" %(devicePath, used))
-        # FIXME: should we bump this beyond the absolute minimum?
-        return used
-
-    def labelDevice(self, entry, chroot):
-        devicePath = entry.device.setupDevice(chroot)
-        label = self.createLabel(entry.mountpoint, self.maxLabelChars,
-                                 kslabel = entry.label)
-
-        rc = iutil.execWithRedirect("e2label",
-                                    [devicePath, label],
-                                    stdout = "/dev/tty5",
-                                    stderr = "/dev/tty5", searchPath = 1)
-        if rc:
-            raise SystemError
-        entry.setLabel(label)
-
-    def formatDevice(self, entry, progress, chroot='/'):
-        devicePath = entry.device.setupDevice(chroot)
-        devArgs = self.getDeviceArgs(entry.device)
-        args = [ "mke2fs", devicePath ]
-
-        fsProfileArgs = self.getFsProfileArgs()
-        if fsProfileArgs:
-            args.extend(fsProfileArgs)
-        args.extend(devArgs)
-        args.extend(self.extraFormatArgs)
-
-        log.info("Format command:  %s\n" % str(args))
-
-        rc = ext2FormatFilesystem(args, "/dev/tty5",
-                                  progress,
-                                  entry.mountpoint)
-        if rc:
-            raise SystemError
-
-    def clobberDevice(self, entry, chroot):
-        device = entry.device.setupDevice(chroot)
-        isys.ext2Clobber(device)
-
-    # this is only for ext3 filesystems, but migration is a method
-    # of the ext2 fstype, so it needs to be here.  FIXME should be moved
-    def setExt3Options(self, entry, message, chroot='/'):
-        devicePath = entry.device.setupDevice(chroot)
-
-        # if no journal, don't turn off the fsck
-        if not isys.ext2HasJournal(devicePath):
-            return
-
-        rc = iutil.execWithRedirect("tune2fs",
-                                    ["-c0", "-i0",
-                                     "-ouser_xattr,acl", devicePath],
-                                    stdout = "/dev/tty5",
-                                    stderr = "/dev/tty5", searchPath = 1)
-
-class ext2FileSystem(extFileSystem):
-    def __init__(self):
-        extFileSystem.__init__(self)
-        self.name = "ext2"
-        self.partedFileSystemType = parted.fileSystemType["ext2"]
-        self.migratetofs = ['ext3']
-
-    def migrateFileSystem(self, entry, message, chroot='/'):
-        devicePath = entry.device.setupDevice(chroot)
-
-        if not entry.fsystem or not entry.origfsystem:
-            raise RuntimeError, ("Trying to migrate fs w/o fsystem or "
-                                 "origfsystem set")
-        if entry.fsystem.getName() != "ext3":
-            raise RuntimeError, ("Trying to migrate ext2 to something other "
-                                 "than ext3")
-
-        # if journal already exists skip
-        if isys.ext2HasJournal(devicePath):
-            log.info("Skipping migration of %s, has a journal already.\n" % devicePath)
-            return
-
-        rc = iutil.execWithRedirect("tune2fs",
-                                    ["-j", devicePath ],
-                                    stdout = "/dev/tty5",
-                                    stderr = "/dev/tty5", searchPath = 1)
-
-        if rc:
-            raise SystemError
-
-        # XXX this should never happen, but appears to have done
-        # so several times based on reports in bugzilla.
-        # At least we can avoid leaving them with a system which won't boot
-        if not isys.ext2HasJournal(devicePath):
-            log.warning("Migration of %s attempted but no journal exists after "
-                        "running tune2fs.\n" % (devicePath))
-            if message:
-                rc = message(_("Error"),
-                        _("An error occurred migrating %s to ext3.  It is "
-                          "possible to continue without migrating this "
-                          "file system if desired.\n\n"
-                          "Would you like to continue without migrating %s?")
-                             % (devicePath, devicePath), type = "yesno")
-                if rc == 0:
-                    sys.exit(0)
-            entry.fsystem = entry.origfsystem
-        else:
-            extFileSystem.setExt3Options(self, entry, message, chroot)
-
-
-fileSystemTypeRegister(ext2FileSystem())
-
-class ext3FileSystem(extFileSystem):
-    def __init__(self):
-        extFileSystem.__init__(self)
-        self.name = "ext3"
-        self.extraFormatArgs = [ "-t", "ext3" ]
-        self.partedFileSystemType = parted.fileSystemType["ext3"]
-        if flags.cmdline.has_key("ext4migrate"):
-            self.migratetofs = ['ext4']
-
-    def formatDevice(self, entry, progress, chroot='/'):
-        extFileSystem.formatDevice(self, entry, progress, chroot)
-        extFileSystem.setExt3Options(self, entry, progress, chroot)
-
-    def migrateFileSystem(self, entry, message, chroot='/'):
-        devicePath = entry.device.setupDevice(chroot)
-
-        if not entry.fsystem or not entry.origfsystem:
-            raise RuntimeError, ("Trying to migrate fs w/o fsystem or "
-                                 "origfsystem set")
-        if entry.fsystem.getName() != "ext4":
-            raise RuntimeError, ("Trying to migrate ext3 to something other "
-                                 "than ext4")
-
-fileSystemTypeRegister(ext3FileSystem())
-
-class ext4FileSystem(extFileSystem):
-    def __init__(self):
-        extFileSystem.__init__(self)
-        self.name = "ext4"
-        self.partedFileSystemType = parted.fileSystemType["ext3"]
-        self.extraFormatArgs = [ "-t", "ext4" ]
-        self.bootable = False
-
-    def formatDevice(self, entry, progress, chroot='/'):
-        extFileSystem.formatDevice(self, entry, progress, chroot)
-        extFileSystem.setExt3Options(self, entry, progress, chroot)
-
-fileSystemTypeRegister(ext4FileSystem())
-
-class btrfsFileSystem(FileSystemType):
-    def __init__(self):
-        FileSystemType.__init__(self)
-        self.formattable = 1
-        self.checked = 1
-        self.linuxnativefs = 1
-        self.bootable = False
-        self.maxLabelChars = 256
-        # Wow, you must be brave!
-        # this is totally, 100% unsupported.  Boot with "linux btrfs"
-        # at the boot: prompt will let you make new btrfs filesystems
-        # in the installer.
-        if flags.cmdline.has_key("icantbelieveitsnotbtr"):
-            self.supported = -1
-        else:
-            self.supported = 0
-
-        self.name = "btrfs"
-        self.packages = [ "btrfs-progs" ]
-        self.needProgram = [ "mkfs.btrfs", "btrfsctl" ]
-
-	# Bigger, really, depending on machine
-        self.maxSizeMB = 16 * 1024 * 1024
-
-    # We'll sneakily label it here, too.
-    def formatDevice(self, entry, progress, chroot='/'):
-        devicePath = entry.device.setupDevice(chroot)
-        label = self.createLabel(entry.mountpoint, self.maxLabelChars,
-                                 kslabel = entry.label)
-
-        rc = iutil.execWithRedirect("mkfs.btrfs",
-				    ["-L", label, devicePath],
-                                    stdout = "/dev/tty5",
-                                    stderr = "/dev/tty5", searchPath = 1)
-
-        if rc:
-            raise SystemError
-        entry.setLabel(label)
-    def labelDevice(self, entry, chroot):
-        # We did this on the initial format; no standalone labeler yet
-        pass
-
-    def resize(self, entry, size, progress, chroot='/'):
-        devicePath = entry.device.setupDevice(chroot)
-        log.info("resizing %s" %(devicePath,))
-
-        w = None
-        if progress:
-            w = progress(_("Resizing"),
-                         _("Resizing filesystem on %s...") %(devicePath),
-                         100, pulse = True)
-
-        rc = iutil.execWithPulseProgress("btrfsctl",
-                                         ["-r", "%sM" %(size,), devicePath],
-                                         stdout="/tmp/resize.out",
-                                         stderr="/tmp/resize.out",
-                                         progress = w)
-        if progress:
-            w.pop()
-        if rc:
-            raise ResizeError, ("Resize of %s failed: %s" %(devicePath, rc), devicePath)
-
-
-fileSystemTypeRegister(btrfsFileSystem())
-
-class raidMemberDummyFileSystem(FileSystemType):
-    def __init__(self):
-        FileSystemType.__init__(self)
-        self.partedFileSystemType = parted.fileSystemType["ext2"]
-        self.partedPartitionFlags = [ parted.PARTITION_RAID ]
-        self.formattable = 1
-        self.checked = 0
-        self.linuxnativefs = 1
-        self.name = "software RAID"
-        self.maxSizeMB = 8 * 1024 * 1024
-        self.supported = 1
-
-        if len(raid.availRaidLevels) == 0:
-            self.supported = 0
-
-        self.packages = [ "mdadm" ]
-
-    def formatDevice(self, entry, progress, chroot='/'):
-        # mkraid did all we need to format this partition...
-        pass
-
-fileSystemTypeRegister(raidMemberDummyFileSystem())
-
-class lvmPhysicalVolumeDummyFileSystem(FileSystemType):
-    def __init__(self):
-        FileSystemType.__init__(self)
-        self.partedFileSystemType = parted.fileSystemType["ext2"]
-        self.partedPartitionFlags = [ parted.PARTITION_LVM ]
-        self.formattable = 1
-        self.checked = 0
-        self.linuxnativefs = 1
-        self.name = "physical volume (LVM)"
-        self.maxSizeMB = 8 * 1024 * 1024
-        self.supported = 1
-        self.packages = [ "lvm2" ]
-
-    def isMountable(self):
-        return 0
-
-    def formatDevice(self, entry, progress, chroot='/'):
-        # already done by the pvcreate during volume creation
-        pass
-
-fileSystemTypeRegister(lvmPhysicalVolumeDummyFileSystem())
-
-class lvmVolumeGroupDummyFileSystem(FileSystemType):
-    def __init__(self):
-        FileSystemType.__init__(self)
-        self.partedFileSystemType = parted.fileSystemType["ext2"]
-        self.formattable = 1
-        self.checked = 0
-        self.linuxnativefs = 0
-        self.name = "volume group (LVM)"
-        self.supported = 0
-        self.maxSizeMB = 8 * 1024 * 1024
-        self.packages = [ "lvm2" ]
-
-    def isMountable(self):
-        return 0
-
-    def formatDevice(self, entry, progress, chroot='/'):
-        # the vgcreate already did this
-        pass
-
-fileSystemTypeRegister(lvmVolumeGroupDummyFileSystem())
-
-class swapFileSystem(FileSystemType):
-    enabledSwaps = {}
-
-    def __init__(self):
-        FileSystemType.__init__(self)
-        self.partedFileSystemType = parted.fileSystemType["linux-swap"]
-        self.formattable = 1
-        self.name = "swap"
-        self.maxSizeMB = 8 * 1024 * 1024
-        self.linuxnativefs = 1
-        self.supported = 1
-        self.maxLabelChars = 15
-
-    def mount(self, device, mountpoint, readOnly=0, bindMount=0,
-              instroot = None):
-        pagesize = resource.getpagesize()
-        buf = None
-        if pagesize > 2048:
-            num = pagesize
-        else:
-            num = 2048
-        try:
-            fd = os.open(device, os.O_RDONLY)
-            buf = os.read(fd, num)
-        except:
-            pass
-        finally:
-            try:
-                os.close(fd)
-            except:
-                pass
-
-        if buf is not None and len(buf) == pagesize:
-            sig = buf[pagesize - 10:]
-            if sig == 'SWAP-SPACE':
-                raise OldSwapError
-            if sig == 'S1SUSPEND\x00' or sig == 'S2SUSPEND\x00':
-                raise SuspendError
-
-        isys.swapon (device)
-
-    def umount(self, device, path):
-        if os.path.exists("/dev/" + device.device):
-            swapFile = os.path.realpath("/dev/" + device.device)
-        else:
-            # path is something like /mnt/sysimage/swap, which is not very
-            # useful.  But since we don't have instPath anywhere else, so
-            # we have to pull it out of path and add the real swap file to
-            # the end of it.
-            swapFile = os.path.realpath(os.path.dirname(path) + "/" + device.device)
-
-        try:
-            iutil.execWithRedirect("swapoff", [swapFile], stdout="/dev/tty5",
-                                   stderr="/dev/tty5", searchPath=1)
-        except:
-            raise RuntimeError, "unable to turn off swap"
-
-    def formatDevice(self, entry, progress, chroot='/'):
-        file = entry.device.setupDevice(chroot)
-        rc = iutil.execWithRedirect ("mkswap",
-                                     ['-v1', file],
-                                     stdout = "/dev/tty5",
-                                     stderr = "/dev/tty5",
-                                     searchPath = 1)
-        if rc:
-            raise SystemError
-
-    def labelDevice(self, entry, chroot):
-        file = entry.device.setupDevice(chroot)
-        devName = entry.device.getDevice()
-        # we'll keep the SWAP-* naming for all devs but Compaq SMART2
-        # nodes (#176074)
-        if devName[0:6] == "cciss/":
-            swapLabel = "SW-%s" % (devName)
-        elif devName.startswith("mapper/"):
-            swapLabel = "SWAP-%s" % (devName[7:],)
-        else:
-            swapLabel = "SWAP-%s" % (devName)
-        label = self.createLabel(swapLabel, self.maxLabelChars)
-        rc = iutil.execWithRedirect ("mkswap",
-                                     ['-v1', "-L", label, file],
-                                     stdout = "/dev/tty5",
-                                     stderr = "/dev/tty5",
-                                     searchPath = 1)
-        if rc:
-            raise SystemError
-        entry.setLabel(label)
-
-    def clobberDevice(self, entry, chroot):
-        pagesize = resource.getpagesize()
-        dev = entry.device.setupDevice(chroot)
-        try:
-            fd = os.open(dev, os.O_RDWR)
-            buf = "\0x00" * pagesize
-            os.write(fd, buf)
-        except:
-            pass
-        finally:
-            try:
-                os.close(fd)
-            except:
-                pass
-
-fileSystemTypeRegister(swapFileSystem())
-
-class FATFileSystem(FileSystemType):
-    def __init__(self):
-        FileSystemType.__init__(self)
-        self.partedFileSystemType = parted.fileSystemType["fat32"]
-        self.formattable = 1
-        self.checked = 0
-        self.maxSizeMB = 1024 * 1024
-        self.name = "vfat"
-        self.packages = [ "dosfstools" ]
-        self.defaultOptions = "umask=0077,shortname=winnt"
-
-    def formatDevice(self, entry, progress, chroot='/'):
-        devicePath = entry.device.setupDevice(chroot)
-        devArgs = self.getDeviceArgs(entry.device)
-        args = [ devicePath ]
-        args.extend(devArgs)
-
-        rc = iutil.execWithRedirect("mkdosfs", args,
-                                    stdout = "/dev/tty5",
-                                    stderr = "/dev/tty5", searchPath = 1)
-        if rc:
-            raise SystemError
-
-    def labelDevice(self, entry, chroot):
-        devicePath = entry.device.setupDevice(chroot)
-        label = self.createLabel(entry.mountpoint, self.maxLabelChars,
-                                 kslabel = entry.label)
-
-        rc = iutil.execWithRedirect("dosfslabel",
-                                    [devicePath, label],
-                                    stdout = "/dev/tty5",
-                                    stderr = "/dev/tty5",
-                                    searchPath = 1)
-        if rc:
-            msg = iutil.execWithCapture("dosfslabel", [devicePath],
-                                        stderr="/dev/tty5")
-            raise SystemError, "dosfslabel failed on device %s: %s" % (devicePath, msg)
-
-        newLabel = iutil.execWithCapture("dosfslabel", [devicePath],
-                                         stderr = "/dev/tty5")
-        newLabel = newLabel.strip()
-        if label != newLabel:
-            raise SystemError, "dosfslabel failed on device %s" % (devicePath,)
-        entry.setLabel(label)
-
-fileSystemTypeRegister(FATFileSystem())
-
-class EFIFileSystem(FATFileSystem):
-    def __init__(self):
-        FATFileSystem.__init__(self)
-        self.name = "efi"
-        self.partedPartitionFlags = [ parted.PARTITION_BOOT ]
-        self.maxSizeMB = 256
-        self.defaultOptions = "umask=0077,shortname=winnt"
-        self.bootable = True
-        if not iutil.isEfi():
-            self.supported = 0
-
-    def getMountName(self, quoted = 0):
-        return "vfat"
-
-    def formatDevice(self, entry, progress, chroot='/'):
-        FATFileSystem.formatDevice(self, entry, progress, chroot)
-
-        # XXX need to set the name in GPT
-        # entry.device.something.part.set_name("EFI System Partition")
-        devicePath = entry.device.setupDevice(chroot)
-
-fileSystemTypeRegister(EFIFileSystem())
-
-class NTFSFileSystem(FileSystemType):
-    def __init__(self):
-        FileSystemType.__init__(self)
-        self.partedFileSystemType = parted.fileSystemType["ntfs"]
-        self.formattable = 0
-        self.checked = 0
-        self.name = "ntfs"
-        if len(filter(lambda d: os.path.exists("%s/ntfsresize" %(d,)),
-                      os.environ["PATH"].split(":"))) > 0:
-            self.resizable = True
-        if len(filter(lambda d: os.path.exists("%s/mount.ntfs-3g" %(d,)),
-                      os.environ["PATH"].split(":"))) > 0:
-            self.fusefs = 1
-
-    def resize(self, entry, size, progress, chroot='/'):
-        devicePath = entry.device.setupDevice(chroot)
-        log.info("resizing %s to %sM" %(devicePath, size))
-        w = None
-        if progress:
-            w = progress(_("Resizing"),
-                         _("Resizing filesystem on %s...") %(devicePath),
-                         100, pulse = True)
-
-        p = os.pipe()
-        os.write(p[1], "y\n")
-        os.close(p[1])
-
-        # FIXME: we should call ntfsresize -c to ensure that we can resize
-        # before starting the operation
-
-        rc = iutil.execWithPulseProgress("ntfsresize", ["-v",
-                                                        "-s", "%sM" %(size,),
-                                                        devicePath],
-                                         stdin = p[0],
-                                         stdout = "/tmp/resize.out",
-                                         stderr = "/tmp/resize.out",
-                                         progress = w)
-        if progress:
-            w.pop()
-        if rc:
-            raise ResizeError, ("Resize of %s failed" %(devicePath,), devicePath)
-
-    def getMinimumSize(self, device):
-        """Return the minimum filesystem size in megabytes"""
-        devicePath = "/dev/%s" % (device,)
-
-        buf = iutil.execWithCapture("ntfsresize", ["-m", devicePath],
-                                    stderr = "/dev/tty5")
-        for l in buf.split("\n"):
-            if not l.startswith("Minsize"):
-                continue
-            try:
-                min = l.split(":")[1].strip()
-                return int(min) + 250
-            except Exception, e:
-                log.warning("Unable to parse output for minimum size on %s: %s" %(device, e))
-
-        log.warning("Unable to discover minimum size of filesystem on %s" %(device,))        
-        return 1
-
-
-fileSystemTypeRegister(NTFSFileSystem())
-
-class hfsFileSystem(FileSystemType):
-    def __init__(self):
-        FileSystemType.__init__(self)
-        self.partedFileSystemType = parted.fileSystemType["hfs"]
-        self.formattable = 1
-        self.checked = 0
-        self.name = "hfs"
-        self.supported = 0
-        self.needProgram = [ "hformat" ]
-
-    def isMountable(self):
-        return 0
-
-    def formatDevice(self, entry, progress, chroot='/'):
-        devicePath = entry.device.setupDevice(chroot)
-        devArgs = self.getDeviceArgs(entry.device)
-        args = [ devicePath ]
-        args.extend(devArgs)
-
-        rc = iutil.execWithRedirect("hformat", args,
-                                    stdout = "/dev/tty5",
-                                    stderr = "/dev/tty5", searchPath = 1)
-        if rc:
-            raise SystemError
-
-fileSystemTypeRegister(hfsFileSystem())
-
-class HfsPlusFileSystem(FileSystemType):
-    def __init__(self):
-        FileSystemType.__init__(self)
-        self.partedFileSystemType = parted.fileSystemType["hfs+"]
-        self.formattable = 0
-        self.checked = 0
-        self.name = "hfs+"
-
-fileSystemTypeRegister(HfsPlusFileSystem())
-
-class applebootstrapFileSystem(hfsFileSystem):
-    def __init__(self):
-        hfsFileSystem.__init__(self)
-        self.partedPartitionFlags = [ parted.PARTITION_BOOT ]
-        self.maxSizeMB = 1
-        self.name = "Apple Bootstrap"
-        self.bootable = True
-        if iutil.getPPCMacGen() == "NewWorld":
-            self.linuxnativefs = 1
-            self.supported = 1
-        else:
-            self.linuxnativefs = 0
-            self.supported = 0
-
-fileSystemTypeRegister(applebootstrapFileSystem())
-
-class prepbootFileSystem(FileSystemType):
-    def __init__(self):
-        FileSystemType.__init__(self)
-        self.partedFileSystemType = None
-        self.partedPartitionFlags = [ parted.PARTITION_BOOT, parted.PARTITION_PREP ]
-        self.checked = 0
-        self.name = "PPC PReP Boot"
-        self.maxSizeMB = 10
-        self.bootable = True
-
-        if iutil.getPPCMachine() == "iSeries":
-            self.maxSizeMB = 64
-
-        # supported for use on the pseries
-        if (iutil.getPPCMachine() == "pSeries" or
-            iutil.getPPCMachine() == "iSeries"):
-            self.linuxnativefs = 1
-            self.supported = 1
-            self.formattable = 1
-        else:
-            self.linuxnativefs = 0
-            self.supported = 0
-            self.formattable = 0
-
-    def formatDevice(self, entry, progress, chroot='/'):
-        return
-
-fileSystemTypeRegister(prepbootFileSystem())
-
-class networkFileSystem(FileSystemType):
-    def __init__(self):
-        FileSystemType.__init__(self)
-        self.formattable = 0
-        self.checked = 0
-        self.name = "nfs"
-
-    def isMountable(self):
-        return 0
-
-fileSystemTypeRegister(networkFileSystem())
-
-class nfsv4FileSystem(FileSystemType):
-    def __init__(self):
-        FileSystemType.__init__(self)
-        self.formattable = 0
-        self.checked = 0
-        self.name = "nfs4"
-
-    def isMountable(self):
-        return 0
-
-fileSystemTypeRegister(nfsv4FileSystem())
-
-class ForeignFileSystem(FileSystemType):
-    def __init__(self):
-        FileSystemType.__init__(self)
-        self.formattable = 0
-        self.checked = 0
-        self.name = "foreign"
-
-    def formatDevice(self, entry, progress, chroot='/'):
-        return
-
-fileSystemTypeRegister(ForeignFileSystem())
-
-class PseudoFileSystem(FileSystemType):
-    def __init__(self, name):
-        FileSystemType.__init__(self)
-        self.formattable = 0
-        self.checked = 0
-        self.name = name
-        self.supported = 0
-
-    def isKernelFS(self):
-        return True
-
-class SpuFileSystem(PseudoFileSystem):
-    def __init__(self):
-        PseudoFileSystem.__init__(self, "spufs")
-
-fileSystemTypeRegister(SpuFileSystem())
-
-class ProcFileSystem(PseudoFileSystem):
-    def __init__(self):
-        PseudoFileSystem.__init__(self, "proc")
-
-fileSystemTypeRegister(ProcFileSystem())
-
-class SysfsFileSystem(PseudoFileSystem):
-    def __init__(self):
-        PseudoFileSystem.__init__(self, "sysfs")
-
-fileSystemTypeRegister(SysfsFileSystem())
-
-class SelinuxfsFileSystem(PseudoFileSystem):
-    def __init__(self):
-        PseudoFileSystem.__init__(self, "selinuxfs")
-
-fileSystemTypeRegister(SelinuxfsFileSystem())
-
-class DevptsFileSystem(PseudoFileSystem):
-    def __init__(self):
-        PseudoFileSystem.__init__(self, "devpts")
-        self.defaultOptions = "gid=5,mode=620"
-
-    def isMountable(self):
-        return 0
-
-fileSystemTypeRegister(DevptsFileSystem())
-
-class DevshmFileSystem(PseudoFileSystem):
-    def __init__(self):
-        PseudoFileSystem.__init__(self, "tmpfs")
-
-    def isMountable(self):
-        return 0
-
-fileSystemTypeRegister(DevshmFileSystem())
-
-class AutoFileSystem(PseudoFileSystem):
-    def __init__(self):
-        PseudoFileSystem.__init__(self, "auto")
-
-    def mount(self, device, mountpoint, readOnly=0, bindMount=0,
-              instroot = None):
-        errNum = 0
-        errMsg = "cannot mount auto filesystem on %s of this type" % device
-
-        if not self.isMountable():
-            return
-        iutil.mkdirChain("%s/%s" %(instroot, mountpoint))
-        if flags.selinux:
-            ret = isys.resetFileContext(mountpoint, instroot)
-            log.info("set SELinux context for mountpoint %s to %s" %(mountpoint, ret))
-
-        fs = isys.readFSType(device)
-        if fs is not None:
-            try:
-                isys.mount (device, mountpoint, fstype = fs, readOnly =
-                            readOnly, bindMount = bindMount)
-                return
-            except SystemError, (num, msg):
-                errNum = num
-                errMsg = msg
-
-        raise SystemError (errNum, errMsg)
-
-    def umount(self, device, path):
-        isys.umount(path, removeDir = 0)
-
-fileSystemTypeRegister(AutoFileSystem())
-
-class BindFileSystem(PseudoFileSystem):
-    def __init__(self):
-        PseudoFileSystem.__init__(self, "bind")
-
-    def isMountable(self):
-        return 1
-
-fileSystemTypeRegister(BindFileSystem())
-
-class FileSystemSet:
-    def __init__(self):
-        self.messageWindow = None
-        self.progressWindow = None
-        self.waitWindow = None
-        self.mountcount = 0
-        self.migratedfs = 0
-        self.reset()
-        self.volumesCreated = 0
-
-    def isActive(self):
-        return self.mountcount != 0
-
-    def registerMessageWindow(self, method):
-        self.messageWindow = method
-
-    def registerProgressWindow(self, method):
-        self.progressWindow = method
-
-    def registerWaitWindow(self, method):
-        self.waitWindow = method
-
-    def reset (self):
-        self.entries = []
-        proc = FileSystemSetEntry(Device(device="proc"), '/proc',
-                                  fileSystemTypeGet("proc"))
-        self.add(proc)
-        sys = FileSystemSetEntry(Device(device="sys"), '/sys',
-                                 fileSystemTypeGet("sysfs"))
-        self.add(sys)
-        pts = FileSystemSetEntry(Device(device="devpts"), '/dev/pts',
-                                 fileSystemTypeGet("devpts"), "gid=5,mode=620")
-        self.add(pts)
-        shm = FileSystemSetEntry(Device(device="shm"), '/dev/shm',
-                                 fileSystemTypeGet("tmpfs"))
-        self.add(shm)
-
-        if iutil.isCell():
-            spu = FileSystemSetEntry(Device(device="spufs"), '/spu',
-                                 fileSystemTypeGet("spufs"))
-            self.add(spu)
-
-    def verify (self):
-        for entry in self.entries:
-            if type(entry.__dict__) != type({}):
-                raise RuntimeError, "fsset internals inconsistent"
-
-    def add (self, newEntry):
-        # Should object A be sorted after object B?  Take mountpoints and
-        # device names into account so bind mounts are sorted correctly.
-        def comesAfter (a, b):
-            mntA = a.mountpoint
-            mntB = b.mountpoint
-            devA = a.device.getDevice()
-            devB = b.device.getDevice()
-
-            if not mntB:
-                return False
-            if mntA and mntA != mntB and mntA.startswith(mntB):
-                return True
-            if devA and devA != mntB and devA.startswith(mntB):
-                return True
-            return False
-
-        def samePseudo (a, b):
-            return isinstance(a.fsystem, PseudoFileSystem) and isinstance (b.fsystem, PseudoFileSystem) and \
-                   not isinstance (a.fsystem, BindFileSystem) and not isinstance (b.fsystem, BindFileSystem) and \
-                   a.fsystem.getName() == b.fsystem.getName()
-
-        def sameEntry (a, b):
-            return a.device.getDevice() == b.device.getDevice() and a.mountpoint == b.mountpoint
-
-        # Remove preexisting duplicate entries - pseudo filesystems are
-        # duplicate if they have the same filesystem type as an existing one.
-        # Otherwise, they have to have the same device and mount point
-        # (required to check for bind mounts).
-        for existing in self.entries:
-            if samePseudo (newEntry, existing) or sameEntry (newEntry, existing):
-                self.remove(existing)
-
-        # XXX debuggin'
-##         log.info ("fsset at %s\n"
-##                   "adding entry for %s\n"
-##                   "entry object %s, class __dict__ is %s",
-##                   self, entry.mountpoint, entry,
-##                   isys.printObject(entry.__dict__))
-
-        insertAt = 0
-
-        # Special case for /.
-        if newEntry.mountpoint == "/":
-            self.entries.insert(insertAt, newEntry)
-            return
-
-        # doesn't matter where these get added, so just put them at the end
-        if not newEntry.mountpoint or not newEntry.mountpoint.startswith("/") or self.entries == []:
-            self.entries.append(newEntry)
-            return
-
-        for entry in self.entries:
-            if comesAfter(newEntry, entry):
-                insertAt = self.entries.index(entry)+1
-
-        self.entries.insert(insertAt, newEntry)
-
-    def remove (self, entry):
-        self.entries.remove(entry)
-
-    def getEntryByMountPoint(self, mount):
-        for entry in self.entries:
-            if entry.mountpoint == mount:
-                return entry
-        return None
-
-    def getEntryByDeviceName(self, dev):
-        for entry in self.entries:
-            if entry.device.getDevice() == dev:
-                return entry
-
-            # getDevice() will return the mapped device if using LUKS
-            if entry.device.device == dev:
-                return entry
-
-        return None
-
-    def copy (self):
-        new = FileSystemSet()
-        for entry in self.entries:
-            new.add (entry)
-        return new
-
-    def fstab (self):
-        format = "%-23s %-23s %-7s %-15s %d %d\n"
-        fstab = """
-#
-# /etc/fstab
-# Created by anaconda on %s
-#
-# Accessible filesystems, by reference, are maintained under '/dev/disk'
-# See man pages fstab(5), findfs(8), mount(8) and/or vol_id(8) for more info
-#
-""" % time.asctime()
-
-        for entry in self.entries:
-            if entry.mountpoint:
-                if entry.getUuid() and entry.device.doLabel is not None:
-                    device = "UUID=%s" %(entry.getUuid(),)
-                elif entry.getLabel() and entry.device.doLabel is not None:
-                    device = "LABEL=%s" % (entry.getLabel(),)
-                else:
-                    device = devify(entry.device.getDevice())
-                fstab = fstab + entry.device.getComment()
-                fstab = fstab + format % (device, entry.mountpoint,
-                                          entry.fsystem.getMountName(),
-                                          entry.getOptions(), entry.fsck,
-                                          entry.order)
-        return fstab
-
-    def mtab (self):
-        format = "%s %s %s %s 0 0\n"
-        mtab = ""
-        for entry in self.entries:
-            if not entry.isMounted():
-                continue
-            if entry.mountpoint:
-                # swap doesn't end up in the mtab
-                if entry.fsystem.getName() == "swap":
-                    continue
-                options = entry.getOptions()
-                if options:
-                    options = "rw," + options
-                else:
-                    options = "rw"
-                mtab = mtab + format % (devify(entry.device.getDevice()),
-                                        entry.mountpoint,
-                                        entry.fsystem.getName(),
-                                        options)
-        return mtab
-
-    def raidtab(self):
-        # set up raidtab...
-        raidtab = ""
-        for entry in self.entries:
-            if entry.device.getName() == "RAIDDevice":
-                raidtab = raidtab + entry.device.raidTab()
-
-        return raidtab
-
-    def mdadmConf(self):
-        """Make the mdadm.conf file with mdadm command.
-
-        This creates a conf file with active arrays.  In other words
-        the arrays that we don't want included must be inactive.
-        """
-        activeArrays = iutil.execWithCapture("mdadm", ["--detail", "--scan"])
-        if len(activeArrays) == 0:
-            return
-
-        cf = """
-# mdadm.conf written out by anaconda
-DEVICE partitions
-MAILADDR root
-
-%s
-""" % activeArrays
-        return cf
-
-    def crypttab(self):
-        """set up /etc/crypttab"""
-        crypttab = ""
-        for entry in self.entries:
-            if entry.device.crypto:
-                crypttab += entry.device.crypto.crypttab()
-
-        return crypttab
-
-    def write (self, prefix):
-        f = open (prefix + "/etc/fstab", "w")
-        f.write (self.fstab())
-        f.close ()
-
-        cf = self.mdadmConf()
-
-        if cf:
-            f = open (prefix + "/etc/mdadm.conf", "w")
-            f.write (cf)
-            f.close ()
-
-        crypttab = self.crypttab()
-        if crypttab:
-            f = open(prefix + "/etc/crypttab", "w")
-            f.write(crypttab)
-            f.close()
-
-        # touch mtab
-        open (prefix + "/etc/mtab", "w+")
-        f.close ()
-
-    def mkDevRoot(self, instPath):
-        root = self.getEntryByMountPoint("/")
-        dev = "%s/dev/%s" % (instPath, root.device.getDevice())
-        if not os.path.exists("%s/dev/root" %(instPath,)) and os.path.exists(dev):
-            rdev = os.stat(dev).st_rdev
-            os.mknod("%s/dev/root" % (instPath,), stat.S_IFBLK | 0600, rdev)
-
-    # return the "boot" device
-    def getBootDev(self):
-        mntDict = {}
-        bootDev = None
-        for entry in self.entries:
-            mntDict[entry.mountpoint] = entry
-
-        # FIXME: this ppc stuff feels kind of crufty -- the abstraction
-        # here needs a little bit of work
-        if iutil.getPPCMacGen() == "NewWorld":
-            for entry in self.entries:
-                if entry.fsystem.getName() == "Apple Bootstrap":
-                    bootDev = entry
-        elif (iutil.getPPCMachine() == "pSeries" or
-              iutil.getPPCMachine() == "iSeries"):
-            # we want the first prep partition or the first newly formatted one
-            bestprep = None
-            for entry in self.entries:
-                if ((entry.fsystem.getName() == "PPC PReP Boot")
-                    and ((bestprep is None) or
-                         ((bestprep.format == 0) and (entry.format == 1)))):
-                    bestprep = entry
-            if bestprep:
-                bootDev = bestprep
-        elif iutil.isEfi():
-            if mntDict.has_key("/boot/efi"):
-                bootDev = mntDict['/boot/efi']
-        elif mntDict.has_key("/boot"):
-            bootDev = mntDict['/boot']
-        elif mntDict.has_key("/"):
-            bootDev = mntDict['/']
-
-        return bootDev
-
-    def bootloaderChoices(self, diskSet, bl):
-        ret = {}
-        bootDev = self.getBootDev()
-
-        if bootDev is None:
-            log.warning("no boot device set")
-            return ret
-
-        if iutil.isEfi():
-            ret['boot'] = (bootDev.device, N_("EFI System Partition"))
-            return ret
-
-        if bootDev.device.getName() == "RAIDDevice":
-            ret['boot'] = (bootDev.device, N_("RAID Device"))
-            ret['mbr'] = (bl.drivelist[0], N_("Master Boot Record (MBR)"))
-            return ret
-
-        if iutil.getPPCMacGen() == "NewWorld":
-            ret['boot'] = (bootDev.device, N_("Apple Bootstrap"))
-            n = 1
-            for entry in self.entries:
-                if ((entry.fsystem.getName() == "Apple Bootstrap") and (
-                    entry.device.getDevice() != bootDev.device)):
-                    ret['boot%d' %(n,)] = (entry.device.getDevice(),
-                                           N_("Apple Bootstrap"))
-                    n = n + 1
-            return ret
-        elif (iutil.getPPCMachine() == "pSeries" or
-              iutil.getPPCMachine() == "iSeries"):
-            ret['boot'] = (bootDev.device, N_("PPC PReP Boot"))
-            return ret
-
-        ret['boot'] = (bootDev.device, N_("First sector of boot partition"))
-        ret['mbr'] = (bl.drivelist[0], N_("Master Boot Record (MBR)"))
-        return ret
-
-    # set active partition on disks
-    # if an active partition is set, leave it alone; if none set
-    # set either our boot partition or the first partition on the drive active
-    def setActive(self, diskset, requests):
-        bootDev = self.getBootDev()
-
-        if bootDev is None:
-            return
-
-        if bootDev.device.getName() != "RAIDDevice":
-            for request in requests:
-                if request.mountpoint == bootDev.mountpoint:
-                    break
-
-            for drive in request.drive:
-                part = diskset.disks[drive].getPartitionByPath("/dev/%s" % bootDev.device.device)
-                if part:
-                    break
-
-            # on EFI systems, *only* /boot/efi should be marked bootable
-            # similarly, on pseries, we really only want the PReP partition
-            # active
-            if iutil.isEfi() \
-                    or iutil.getPPCMachine() in ("pSeries", "iSeries", "PMac") \
-                    or (iutil.isX86() \
-                             and partedUtils.hasGptLabel(diskset, drive)):
-                if part and part.isFlagAvailable(parted.PARTITION_BOOT):
-                    part.setFlag(parted.PARTITION_BOOT)
-                return
-
-        for drive in diskset.disks.keys():
-            foundActive = 0
-            bootPart = None
-            if partedUtils.hasGptLabel(diskset, drive):
-                continue
-            disk = diskset.disks[drive]
-            for part in disk.partitions:
-                if not part.active:
-                    continue
-
-                if not part.isFlagAvailable(parted.PARTITION_BOOT):
-                    foundActive = 1
-                    part = None
-                    continue
-
-                if part.getFlag(parted.PARTITION_BOOT):
-                    foundActive = 1
-                    part = None
-                    continue
-
-                if not bootPart:
-                    bootPart = part
-
-                if part.getDeviceNodeName() == bootDev.device.device:
-                    bootPart = part
-
-            if bootPart and not foundActive:
-                bootPart.setFlag(parted.PARTITION_BOOT)
-
-            if bootPart:
-                del bootPart
-
-    def resizeFilesystems (self, diskset, chroot = '/', shrink = False, grow = False):
-        todo = []
-        for entry in self.entries:
-            if not entry.fsystem or not entry.fsystem.isResizable():
-                continue
-            if entry.fsystem.isFormattable() and entry.getFormat():
-                continue
-            if entry.resizeTargetSize is None:
-                continue
-            if shrink and not (entry.resizeTargetSize < entry.resizeOrigSize):
-                continue
-            if grow and not (entry.resizeTargetSize > entry.resizeOrigSize):
-                continue
-            todo.append(entry)
-        if len(todo) == 0:
-            return
-
-        # we have to have lvm activated to be able to do resizes of LVs
-        lvmActive = lvm.vgcheckactive()
-        devicesActive = diskset.devicesOpen
-
-        if not devicesActive:
-            # should this not be diskset.openDevices() ?
-            diskset.startMPath()
-            diskset.startDmRaid()
-            diskset.startMdRaid()
-
-        if not lvmActive:
-            lvm.vgscan()
-            lvm.vgactivate()
-
-        for entry in todo:
-            entry.fsystem.resize(entry, entry.resizeTargetSize,
-                                 self.progressWindow, chroot)
-        if not lvmActive:
-            lvm.vgdeactivate()
-        
-        if not devicesActive:
-            # should this not be diskset.closeDevices() ?
-            diskset.stopMPath()
-            diskset.stopDmRaid()
-            diskset.stopMdRaid()
-
-    def shrinkFilesystems (self, diskset, chroot):
-        self.resizeFilesystems(diskset, chroot, shrink = True)
-    def growFilesystems (self, diskset, chroot):
-        self.resizeFilesystems(diskset, chroot, grow = True)
-
-    def formatSwap (self, chroot, forceFormat=False):
-        formatted = []
-        notformatted = []
-
-        for entry in self.entries:
-            if (not entry.fsystem or not entry.fsystem.getName() == "swap" or
-                entry.isMounted()):
-                continue
-            if not entry.getFormat():
-                if not forceFormat:
-                    notformatted.append(entry)
-                    continue
-            try:
-                self.formatEntry(entry, chroot)
-                formatted.append(entry)
-            except SystemError:
-                if self.messageWindow:
-                    self.messageWindow(_("Error"),
-                                       _("An error occurred trying to "
-                                         "initialize swap on device %s.  This "
-                                         "problem is serious, and the install "
-                                         "cannot continue.\n\n"
-                                         "Press <Enter> to exit the installer.")
-                                       % (entry.device.getDevice(),))
-                sys.exit(0)
-
-        for entry in formatted:
-            try:
-                self.labelEntry(entry, chroot)
-            except SystemError:
-                # should be OK, fall back to by device
-                pass
-
-        # find if there's a label on the ones we're not formatting
-        for entry in notformatted:
-            dev = entry.device.getDevice()
-            if not dev or dev == "none":
-                continue
-            try:
-                label = isys.readFSLabel(dev)
-            except:
-                continue
-            if label:
-                entry.setLabel(label)
-
-    def turnOnSwap (self, chroot, upgrading=False):
-        def swapErrorDialog (msg, format_button_text, entry):
-            buttons = [_("Skip"), format_button_text, _("_Exit installer")]
-            ret = self.messageWindow(_("Error"), msg, type="custom",
-                                     custom_buttons=buttons,
-                                     custom_icon="warning")
-            if ret == 0:
-                self.entries.remove(entry)
-            elif ret == 1:
-                self.formatEntry(entry, chroot)
-                entry.mount(chroot)
-                self.mountcount = self.mountcount + 1
-            else:
-                sys.exit(0)
-
-        for entry in self.entries:
-            if (entry.fsystem and entry.fsystem.getName() == "swap"
-                and not entry.isMounted()):
-                try:
-                    entry.mount(chroot)
-                    self.mountcount = self.mountcount + 1
-                except OldSwapError:
-                    if self.messageWindow:
-                        msg = _("The swap device:\n\n     /dev/%s\n\n"
-                                "is a version 0 Linux swap partition. If you "
-                                "want to use this device, you must reformat as "
-                                "a version 1 Linux swap partition. If you skip "
-                                "it, the installer will ignore it during the "
-                                "installation.") % (entry.device.getDevice())
-
-                        swapErrorDialog(msg, _("Reformat"), entry)
-                except SuspendError:
-                    if self.messageWindow:
-                        if upgrading:
-                            msg = _("The swap device:\n\n     /dev/%s\n\n"
-                                    "in your /etc/fstab file is currently in "
-                                    "use as a software suspend partition, "
-                                    "which means your system is hibernating. "
-                                    "To perform an upgrade, please shut down "
-                                    "your system rather than hibernating it.") \
-                                  % (entry.device.getDevice())
-                        else:
-                            msg = _("The swap device:\n\n     /dev/%s\n\n"
-                                    "in your /etc/fstab file is currently in "
-                                    "use as a software suspend partition, "
-                                    "which means your system is hibernating. "
-                                    "If you are performing a new install, "
-                                    "make sure the installer is set "
-                                    "to format all swap partitions.") \
-                                  % (entry.device.getDevice())
-
-                        # choose your own adventure swap partitions...
-                        msg = msg + _("\n\nChoose Skip if you want the "
-                              "installer to ignore this partition during "
-                              "the upgrade.  Choose Format to reformat "
-                              "the partition as swap space.")
-
-                        swapErrorDialog(msg, _("Format"), entry)
-                    else:
-                        sys.exit(0)
-                except SystemError, (num, msg):
-                    if self.messageWindow:
-                        if upgrading and not entry.getLabel():
-                            err = _("Error enabling swap device %s: %s\n\n"
-                                    "Devices in /etc/fstab should be specified "
-                                    "by label, not by device name.\n\nPress "
-                                    "OK to exit the installer.") % (entry.device.getDevice(), msg)
-                        elif upgrading:
-                            err = _("Error enabling swap device %s: %s\n\n"
-                                    "The /etc/fstab on your upgrade partition "
-                                    "does not reference a valid swap "
-                                    "partition.\n\nPress OK to exit the "
-                                    "installer") % (entry.device.getDevice(), msg)
-                        else:
-                            err = _("Error enabling swap device %s: %s\n\n"
-                                    "This most likely means this swap "
-                                    "partition has not been initialized.\n\n"
-                                    "Press OK to exit the installer.") % (entry.device.getDevice(), msg)
-
-                    self.messageWindow(_("Error"), err)
-                    sys.exit(0)
-
-    def labelEntry(self, entry, chroot, ignoreExisting = False):
-        label = entry.device.getLabel()
-        if label and not ignoreExisting:
-            entry.setLabel(label)
-            entry.device.doLabel = 1
-
-        if entry.device.doLabel is not None:
-            entry.fsystem.labelDevice(entry, chroot)
-
-    def formatEntry(self, entry, chroot):
-        if entry.mountpoint:
-            log.info("formatting %s as %s" %(entry.mountpoint, entry.fsystem.name))
-        entry.fsystem.clobberDevice(entry, chroot)
-        entry.fsystem.formatDevice(entry, self.progressWindow, chroot)
-
-    def getMigratableEntries(self):
-        retval = []
-        for entry in self.entries:
-            if entry.origfsystem and entry.origfsystem.isMigratable():
-                retval.append(entry)
-
-        return retval
-
-    def formattablePartitions(self):
-        list = []
-        for entry in self.entries:
-            if entry.fsystem.isFormattable():
-                list.append (entry)
-        return list
-
-    def createLogicalVolumes (self, chroot='/'):
-        vgs = {}
-        # first set up the volume groups
-        for entry in self.entries:
-            if entry.fsystem.name == "volume group (LVM)":
-                entry.device.setupDevice(chroot)
-                vgs[entry.device.name] = entry.device
-
-        # then set up the logical volumes
-        for entry in self.entries:
-            if isinstance(entry.device, LogicalVolumeDevice):
-                vg = None
-                if vgs.has_key(entry.device.vgname):
-                    vg = vgs[entry.device.vgname]
-                entry.device.setupDevice(chroot, vgdevice = vg)
-        self.volumesCreated = 1
-
-
-    def makeFilesystems (self, chroot='/', skiprootfs=False):
-        formatted = []
-        notformatted = []
-        for entry in self.entries:
-            if (not entry.fsystem.isFormattable() or not entry.getFormat()
-                or entry.isMounted()):
-                notformatted.append(entry)
-                continue
-            # FIXME: this is a bit of a hack, but works
-            if (skiprootfs and entry.mountpoint == '/'):
-                formatted.append(entry)
-                continue
-            try:
-                self.formatEntry(entry, chroot)
-                formatted.append(entry)
-            except SystemError:
-                if self.messageWindow:
-                    self.messageWindow(_("Error"),
-                                       _("An error occurred trying to "
-                                         "format %s.  This problem is "
-                                         "serious, and the install cannot "
-                                         "continue.\n\n"
-                                         "Press <Enter> to exit the installer.")
-                                       % (entry.device.getDevice(),))
-                sys.exit(0)
-
-        for entry in formatted:
-            try:
-                self.labelEntry(entry, chroot)
-            except SystemError:
-                # should be OK, we'll still use the device name to mount.
-                pass
-
-        # go through and have labels for the ones we don't format
-        for entry in notformatted:
-            dev = entry.device.getDevice()
-            if not dev or dev == "none":
-                continue
-            if not entry.mountpoint or entry.mountpoint == "swap":
-                continue
-            try:
-                label = isys.readFSLabel(dev)
-            except:
-                continue
-
-            if label:
-                entry.setLabel(label)
-            elif entry.fsystem.isSupported():
-                self.labelEntry(entry, chroot)
-
-    def haveMigratedFilesystems(self):
-        return self.migratedfs
-
-    def migrateFilesystems (self, anaconda):
-        if self.migratedfs:
-            return
-
-        for entry in self.entries:
-            if not entry.origfsystem:
-                continue
-
-            if not entry.origfsystem.isMigratable() or not entry.getMigrate():
-                continue
-            try: 
-                entry.origfsystem.migrateFileSystem(entry, self.messageWindow,
-                                                    anaconda.rootPath)
-            except SystemError:
-                if self.messageWindow:
-                    self.messageWindow(_("Error"),
-                                       _("An error occurred trying to "
-                                         "migrate %s.  This problem is "
-                                         "serious, and the install cannot "
-                                         "continue.\n\n"
-                                         "Press <Enter> to exit the installer.")
-                                       % (entry.device.getDevice(),))
-                sys.exit(0)
-
-        # we need to unmount and remount so that we're mounted as the
-        # new fstype as we want to use the new filesystem type during
-        # the upgrade for ext3->ext4 migrations
-        if self.isActive():
-            self.umountFilesystems(anaconda.rootPath)
-            self.mountFilesystems(anaconda)
-            self.turnOnSwap(anaconda.rootPath)
-
-        self.migratedfs = 1
-
-    def mountFilesystems(self, anaconda, raiseErrors = 0, readOnly = 0, skiprootfs = 0):
-        protected = anaconda.id.partitions.protectedPartitions()
-
-        for entry in self.entries:
-            # Don't try to mount a protected partition, since it will already
-            # have been mounted as the installation source.
-            if protected and entry.device.getDevice() in protected and os.path.ismount("/mnt/isodir"):
-                continue
-
-            if not entry.fsystem.isMountable() or (skiprootfs and entry.mountpoint == '/'):
-                continue
-
-            try:
-                log.info("trying to mount %s on %s" %(entry.device.setupDevice(), entry.mountpoint,))
-                entry.mount(anaconda.rootPath, readOnly = readOnly)
-                self.mountcount = self.mountcount + 1
-            except OSError, (num, msg):
-                if self.messageWindow:
-                    if num == errno.EEXIST:
-                        self.messageWindow(_("Invalid mount point"),
-                                           _("An error occurred when trying "
-                                             "to create %s.  Some element of "
-                                             "this path is not a directory. "
-                                             "This is a fatal error and the "
-                                             "install cannot continue.\n\n"
-                                             "Press <Enter> to exit the "
-                                             "installer.") % (entry.mountpoint,))
-                    else:
-                        self.messageWindow(_("Invalid mount point"),
-                                           _("An error occurred when trying "
-                                             "to create %s: %s.  This is "
-                                             "a fatal error and the install "
-                                             "cannot continue.\n\n"
-                                             "Press <Enter> to exit the "
-                                             "installer.") % (entry.mountpoint,
-                                                           msg))
-                log.error("OSError: (%d) %s" % (num, msg) )
-                sys.exit(0)
-            except SystemError, (num, msg):
-                if raiseErrors:
-                    raise SystemError, (num, msg)
-                if self.messageWindow:
-                    if not entry.fsystem.isLinuxNativeFS():
-                        ret = self.messageWindow(_("Unable to mount filesystem"),
-                                                 _("An error occurred mounting "
-                                                 "device %s as %s.  You may "
-                                                 "continue installation, but "
-                                                 "there may be problems.") %
-                                                 (entry.device.getDevice(),
-                                                  entry.mountpoint),
-                                                 type="custom", custom_icon="warning",
-                                                 custom_buttons=[_("_Exit installer"),
-                                                                _("_Continue")])
-
-                        if ret == 0:
-                            sys.exit(0)
-                        else:
-                            continue
-                    else:
-                        if anaconda.id.getUpgrade() and not (entry.getLabel() or entry.getUuid()) and entry.device.getDevice().startswith("/dev"):
-                            errStr = _("Error mounting device %s as %s: "
-                                       "%s\n\n"
-                                       "Devices in /etc/fstab should be specified "
-                                       "by label or UUID, not by device name."
-                                       "\n\n"
-                                       "Press OK to exit the installer.") % (entry.device.getDevice(), entry.mountpoint, msg)
-                        else:
-                            errStr = _("Error mounting device %s as %s: "
-                                       "%s\n\n"
-                                       "Press OK to exit the installer.") % (entry.device.getDevice(), entry.mountpoint, msg)
-
-                        self.messageWindow(_("Error"), errStr)
-
-                log.error("SystemError: (%d) %s" % (num, msg) )
-                sys.exit(0)
-
-        self.makeLVMNodes(anaconda.rootPath)
-
-    def makeLVMNodes(self, instPath, trylvm1 = 0):
-        # XXX hack to make the device node exist for the root fs if
-        # it's a logical volume so that mkinitrd can create the initrd.
-        root = self.getEntryByMountPoint("/")
-        if not root:
-            if self.messageWindow:
-                self.messageWindow(_("Error"),
-                                   _("Error finding / entry.\n\n"
-                                   "This is most likely means that "
-                                   "your fstab is incorrect."
-                                   "\n\n"
-                                   "Press OK to exit the installer."))
-            sys.exit(0)
-
-        rootlvm1 = 0
-        if trylvm1:
-            dev = root.device.getDevice()
-            # lvm1 major is 58
-            if os.access("%s/dev/%s" %(instPath, dev), os.R_OK) and posix.major(os.stat("%s/dev/%s" %(instPath, dev)).st_rdev) == 58:
-                rootlvm1 = 1
-
-        if isinstance(root.device, LogicalVolumeDevice) or rootlvm1:
-            # now make sure all of the device nodes exist.  *sigh*
-            rc = lvm.vgmknodes()
-
-            rootDev = "/dev/%s" % (root.device.getDevice(),)
-            rootdir = instPath + os.path.dirname(rootDev)
-            if not os.path.isdir(rootdir):
-                os.makedirs(rootdir)
-
-            if root.device.crypto is None:
-                dmdev = "/dev/mapper/" + root.device.getDevice().replace("-","--").replace("/", "-")
-            else:
-                dmdev = "/dev/" + root.device.getDevice()
-
-            if os.path.exists(instPath + dmdev):
-                os.unlink(instPath + dmdev)
-            if not os.path.isdir(os.path.dirname(instPath + dmdev)):
-                os.makedirs(os.path.dirname(instPath + dmdev))
-            iutil.copyDeviceNode(dmdev, instPath + dmdev)
-
-            # unlink existing so that we dtrt on upgrades
-            if os.path.exists(instPath + rootDev) and not root.device.crypto:
-                os.unlink(instPath + rootDev)
-            if not os.path.isdir(rootdir):
-                os.makedirs(rootdir)
-
-            if root.device.crypto is None:
-                os.symlink(dmdev, instPath + rootDev)
-
-            if not os.path.isdir("%s/etc/lvm" %(instPath,)):
-                os.makedirs("%s/etc/lvm" %(instPath,))
-
-    def filesystemSpace(self, chroot='/'):
-        space = []
-        for entry in self.entries:
-            if not entry.isMounted():
-                continue
-            # we can't put swap files on swap partitions; that's nonsense
-            if entry.mountpoint == "swap":
-                continue
-            path = "%s/%s" % (chroot, entry.mountpoint)
-            try:
-                space.append((entry.mountpoint, isys.pathSpaceAvailable(path)))
-            except SystemError:
-                log.error("failed to get space available in filesystemSpace() for %s" %(entry.mountpoint,))
-
-        def spaceSort(a, b):
-            (m1, s1) = a
-            (m2, s2) = b
-
-            if (s1 > s2):
-                return -1
-            elif s1 < s2:
-                return 1
-
-            return 0
-
-        space.sort(spaceSort)
-        return space
-
-    def hasDirtyFilesystems(self, mountpoint):
-        ret = []
-
-        for entry in self.entries:
-            # XXX - multifsify, virtualize isdirty per fstype
-            if entry.fsystem.getName() != "ext2": continue
-            if entry.getFormat(): continue
-            if isinstance(entry.device.getDevice(), BindMountDevice): continue
-
-            try:
-                if isys.ext2IsDirty(entry.device.getDevice()):
-                    log.info("%s is a dirty ext2 partition" % entry.device.getDevice())
-                    ret.append(entry.device.getDevice())
-            except Exception, e:
-                log.error("got an exception checking %s for being dirty, hoping it's not" %(entry.device.getDevice(),))
-
-        return ret
-
-    def umountFilesystems(self, instPath, ignoreErrors = 0, swapoff = True):
-        # Unmount things bind mounted into the instPath here because they're
-        # not tracked by self.entries.
-        if os.path.ismount("%s/dev" % instPath):
-            isys.umount("%s/dev" % instPath, removeDir=0)
-
-        # take a slice so we don't modify self.entries
-        reverse = self.entries[:]
-        reverse.reverse()
-
-        for entry in reverse:
-            if entry.mountpoint == "swap" and not swapoff:
-                continue
-            entry.umount(instPath)
-            entry.device.cleanupDevice(instPath)
-
-class FileSystemSetEntry:
-    def __init__ (self, device, mountpoint,
-                  fsystem=None, options=None,
-                  origfsystem=None, migrate=0,
-                  order=-1, fsck=-1, format=0,
-                  fsprofile=None):
-        if not fsystem:
-            fsystem = fileSystemTypeGet("ext2")
-        self.device = device
-        self.mountpoint = mountpoint
-        self.fsystem = fsystem
-        self.origfsystem = origfsystem
-        self.migrate = migrate
-        self.resizeTargetSize = None
-        self.resizeOrigSize = None
-        self.options = options
-        self.mountcount = 0
-        self.label = None
-        if fsck == -1:
-            self.fsck = fsystem.isChecked()
-        else:
-            self.fsck = fsck
-        if order == -1:
-            if mountpoint == '/':
-                self.order = 1
-            elif self.fsck:
-                self.order = 2
-            else:
-                self.order = 0
-        else:
-            self.order = order
-        if format and not fsystem.isFormattable():
-            raise RuntimeError, ("file system type %s is not formattable, "
-                                 "but has been added to fsset with format "
-                                 "flag on" % fsystem.getName())
-        self.format = format
-        self.fsprofile = fsprofile
-
-    def mount(self, chroot='/', devPrefix='/dev', readOnly = 0):
-        device = self.device.setupDevice(chroot, devPrefix=devPrefix)
-
-        self.fsystem.mount(device, "%s" % (self.mountpoint,),
-                           readOnly = readOnly,
-                           bindMount = isinstance(self.device,
-                                                  BindMountDevice),
-                           instroot = chroot)
-
-        self.mountcount = self.mountcount + 1
-
-    def umount(self, chroot='/'):
-        if self.mountcount > 0:
-            try:
-                self.fsystem.umount(self.device, "%s/%s" % (chroot,
-                                                            self.mountpoint))
-                self.mountcount = self.mountcount - 1
-            except RuntimeError:
-                pass
-
-    def setFileSystemType(self, fstype):
-        self.fsystem = fstype
-
-    def getMountPoint(self):
-        return self.mountpoint
-
-    def getOptions(self):
-        options = self.options
-        if not options:
-            options = self.fsystem.getDefaultOptions(self.mountpoint)
-        return options + self.device.getDeviceOptions()
-
-    def setFormat (self, state):
-        if self.migrate and state:
-            raise ValueError, "Trying to set format bit on when migrate is set!"
-        self.format = state
-
-    def getFormat (self):
-        return self.format
-
-    def setMigrate (self, state):
-        if self.format and state:
-            raise ValueError, "Trying to set migrate bit on when format is set!"
-
-        self.migrate = state
-
-    def getMigrate (self):
-        return self.migrate
-
-    def setResizeTarget (self, targetsize, size):
-        if not self.fsystem.isResizable() and targetsize is not None:
-            raise ValueError, "Can't set a resize target for a non-resizable filesystem"
-        self.resizeTargetSize = targetsize
-        self.resizeOrigSize = size
-
-    def getResizeTarget (self):
-        return self.resizeTargetSize
-
-    def isMounted (self):
-        return self.mountcount > 0
-
-    def getLabel (self):
-        return self.label
-
-    def getUuid (self):
-        return isys.readFSUuid(self.device.getDevice())
-
-    def setLabel (self, label):
-        self.label = label
-
-    def __str__(self):
-        if not self.mountpoint:
-            mntpt = "None"
-        else:
-            mntpt = self.mountpoint
-
-        str = ("fsentry -- device: %(device)s   mountpoint: %(mountpoint)s\n"
-               "  fsystem: %(fsystem)s format: %(format)s\n"
-               "  ismounted: %(mounted)s  options: '%(options)s'\n"
-               "  label: %(label)s fsprofile: %(fsprofile)s\n"%
-               {"device": self.device.getDevice(), "mountpoint": mntpt,
-                "fsystem": self.fsystem.getName(), "format": self.format,
-                "mounted": self.mountcount, "options": self.getOptions(),
-                "label": self.label, "fsprofile": self.fsprofile})
-        return str
-
-
-class Device:
-    def __init__(self, device = "none", encryption=None):
-        self.device = device
-        self.label = None
-        self.isSetup = 0
-        self.doLabel = 1
-        self.deviceOptions = ""
-        if encryption:
-            self.crypto = encryption
-            # mount by device since the name is based only on UUID
-            self.doLabel = None
-            if device not in ("none", None):
-                self.crypto.setDevice(device)
-        else:
-            self.crypto = None
-
-    def getComment (self):
-        return ""
-
-    def getDevice (self, asBoot = 0):
-        if self.crypto:
-            return self.crypto.getDevice()
-        else:
-            return self.device
-
-    def setupDevice (self, chroot='/', devPrefix='/dev/'):
-        return self.device
-
-    def cleanupDevice (self, chroot, devPrefix='/dev/'):
-        if self.crypto:
-            self.crypto.closeDevice()
-
-    def solidify (self):
-        pass
-
-    def getName(self):
-        return self.__class__.__name__
-
-    def getLabel(self):
-        try:
-            return isys.readFSLabel(self.setupDevice())
-        except:
-            return ""
-
-    def setAsNetdev(self):
-        """Ensure we're set up so that _netdev is in our device options."""
-        if "_netdev" not in self.deviceOptions:
-            self.deviceOptions += ",_netdev"
-
-    def isNetdev(self):
-        """Check to see if we're set as a netdev"""
-        if "_netdev" in self.deviceOptions:
-            return True
-        return False
-
-    def getDeviceOptions(self):
-        return self.deviceOptions
-
-class DevDevice(Device):
-    """Device with a device node rooted in /dev that we just always use
-       the pre-created device node for."""
-    def __init__(self, dev):
-        Device.__init__(self, device=dev)
-
-    def getDevice(self, asBoot = 0):
-        return self.device
-
-    def setupDevice(self, chroot='/', devPrefix='/dev'):
-        #We use precreated device but we have to make sure that the device exists
-        path = '/dev/%s' % (self.getDevice(),)
-        return path
-
-class RAIDDevice(Device):
-    # XXX usedMajors does not take in account any EXISTING md device
-    #     on the system for installs.  We need to examine all partitions
-    #     to investigate which minors are really available.
-    usedMajors = {}
-
-    # members is a list of Device based instances that will be
-    # a part of this raid device
-    def __init__(self, level, members, minor=-1, spares=0, existing=0,
-                 chunksize = 64, encryption=None):
-        Device.__init__(self, encryption=encryption)
-        self.level = level
-        self.members = members
-        self.spares = spares
-        self.numDisks = len(members) - spares
-        self.isSetup = existing
-        self.doLabel = None
-        if chunksize is not None:
-            self.chunksize = chunksize
-        else:
-            self.chunksize = 256
-
-        if len(members) < spares:
-            raise RuntimeError, ("you requested more spare devices "
-                                 "than online devices!")
-
-        if level == 5:
-            if self.numDisks < 3:
-                raise RuntimeError, "RAID 5 requires at least 3 online members"
-
-        # there are 32 major md devices, 0...31
-        if minor == -1 or minor is None:
-            for I in range(32):
-                if not RAIDDevice.usedMajors.has_key(I):
-                    minor = I
-                    break
-
-            if minor == -1:
-                raise RuntimeError, ("Unable to allocate minor number for "
-                                     "raid device")
-
-        RAIDDevice.usedMajors[minor] = None
-        self.device = "md" + str(minor)
-        self.minor = minor
-
-        if self.crypto:
-            self.crypto.setDevice(self.device)
-
-        # make sure the list of raid members is sorted
-        self.members.sort(cmp=lambda x,y: cmp(x.getDevice(),y.getDevice()))
-
-    def __del__ (self):
-        del RAIDDevice.usedMajors[self.minor]
-
-    def ext2Args (self):
-        if self.level == 5:
-            return [ '-R', 'stride=%d' % ((self.numDisks - 1) * 16) ]
-        elif self.level == 0:
-            return [ '-R', 'stride=%d' % (self.numDisks * 16) ]
-        return []
-
-    def mdadmLine (self, devPrefix="/dev"):
-        levels = { 0: "raid0",
-                   1: "raid1",
-                   4: "raid5",
-                   5: "raid5",
-                   6: "raid6",
-                  10: "raid10" }
-
-        # If we can't find the device for some reason, revert to old behavior.
-        try:
-            (dev, devices, level, numActive) = raid.lookup_raid_device (self.device)
-        except KeyError:
-            devices = []
-
-        # First loop over all the devices that make up the RAID trying to read
-        # the superblock off each.  If we read a superblock, return a line that
-        # can go into the mdadm.conf.  If we fail, fall back to the old method
-        # of using the super-minor.
-        for d in devices:
-            try:
-                (major, minor, uuid, level, nrDisks, totalDisks, mdMinor) = \
-                    isys.raidsb(d)
-                return "ARRAY %s/%s level=%s num-devices=%d uuid=%s\n" \
-                    %(devPrefix, self.device, levels[level], nrDisks, uuid)
-            except ValueError:
-               pass
-
-        return "ARRAY %s/%s super-minor=%s\n" %(devPrefix, self.device,
-                                                self.minor)
-
-    def raidTab (self, devPrefix='/dev'):
-        entry = ""
-        entry = entry + "raiddev		    %s/%s\n" % (devPrefix,
-                                                                self.device,)
-        entry = entry + "raid-level		    %d\n" % (self.level,)
-        entry = entry + "nr-raid-disks		    %d\n" % (self.numDisks,)
-        entry = entry + "chunk-size		    %s\n" %(self.chunksize,)
-        entry = entry + "persistent-superblock	    1\n"
-        entry = entry + "nr-spare-disks		    %d\n" % (self.spares,)
-        i = 0
-        for device in [m.getDevice() for m in self.members[:self.numDisks]]:
-            entry = entry + "    device	    %s/%s\n" % (devPrefix,
-                                                        device)
-            entry = entry + "    raid-disk     %d\n" % (i,)
-            i = i + 1
-        i = 0
-        for device in [m.getDevice() for m in self.members[self.numDisks:]]:
-            entry = entry + "    device	    %s/%s\n" % (devPrefix,
-                                                        device)
-            entry = entry + "    spare-disk     %d\n" % (i,)
-            i = i + 1
-        return entry
-
-    def setupDevice (self, chroot="/", devPrefix='/dev'):
-        if not self.isSetup:
-            memberDevs = []
-            for pd in self.members:
-                memberDevs.append(pd.setupDevice(chroot, devPrefix=devPrefix))
-                if pd.isNetdev(): self.setAsNetdev()
-
-            args = ["--create", "/dev/%s" %(self.device,),
-                    "--run", "--chunk=%s" %(self.chunksize,),
-                    "--level=%s" %(self.level,),
-                    "--raid-devices=%s" %(self.numDisks,)]
-
-            if self.spares > 0:
-                args.append("--spare-devices=%s" %(self.spares,),)
-
-            args.extend(memberDevs)
-            log.info("going to run: %s" %(["mdadm"] + args,))
-            iutil.execWithRedirect ("mdadm", args,
-                                    stderr="/dev/tty5", stdout="/dev/tty5",
-                                    searchPath = 1)
-            raid.register_raid_device(self.device,
-                                      [m.getDevice() for m in self.members],
-                                      self.level, self.numDisks)
-            self.isSetup = 1
-        else:
-            isys.raidstart(self.device, self.members[0].getDevice())
-
-        if self.crypto:
-            self.crypto.formatDevice()
-            self.crypto.openDevice()
-            node = "%s/%s" % (devPrefix, self.crypto.getDevice())
-        else:
-            node = "%s/%s" % (devPrefix, self.device)
-
-        return node
-
-    def getDevice (self, asBoot = 0):
-        if not asBoot and self.crypto:
-            return self.crypto.getDevice()
-        elif not asBoot:
-            return self.device
-        else:
-            return self.members[0].getDevice(asBoot=asBoot)
-
-    def solidify(self):
-        return
-
-ext2 = fileSystemTypeGet("ext2")
-ext2.registerDeviceArgumentFunction(RAIDDevice, RAIDDevice.ext2Args)
-
-class VolumeGroupDevice(Device):
-    def __init__(self, name, physvols, pesize = 32768, existing = 0):
-        """Creates a VolumeGroupDevice.
-
-        name is the name of the volume group
-        physvols is a list of Device objects which are the physical volumes
-        pesize is the size of physical extents in kilobytes
-        existing is whether this vg previously existed.
-        """
-
-        Device.__init__(self)
-        self.physicalVolumes = physvols
-        self.isSetup = existing
-        self.name = name
-        self.device = name
-        self.isSetup = existing
-
-        self.physicalextentsize = pesize
-
-    def setupDevice (self, chroot="/", devPrefix='/dev/'):
-        nodes = []
-        for volume in self.physicalVolumes:
-            # XXX the lvm tools are broken and will only work for /dev
-            node = volume.setupDevice(chroot, devPrefix="/dev")
-            if volume.isNetdev(): self.setAsNetdev()
-
-            # XXX I should check if the pv is set up somehow so that we
-            # can have preexisting vgs and add new pvs to them.
-            if not self.isSetup:
-                lvm.pvcreate(node)
-                nodes.append(node)
-
-        if not self.isSetup:
-            lvm.vgcreate(self.name, self.physicalextentsize, nodes)
-            self.isSetup = 1
-        else:
-            lvm.vgscan()
-            lvm.vgactivate()
-
-        return "/dev/%s" % (self.name,)
-
-    def solidify(self):
-        return
-
-class LogicalVolumeDevice(Device):
-    # note that size is in megabytes!
-    def __init__(self, vgname, size, lvname, vg, existing = 0, encryption=None):
-        Device.__init__(self, encryption=encryption)
-        self.vgname = vgname
-        self.size = size
-        self.name = lvname
-        self.isSetup = 0
-        self.isSetup = existing
-        self.doLabel = None
-        self.vg = vg
-
-        # these are attributes we might want to expose.  or maybe not.
-        # self.chunksize
-        # self.stripes
-        # self.stripesize
-        # self.extents
-        # self.readaheadsectors
-
-    def setupDevice(self, chroot="/", devPrefix='/dev', vgdevice = None):
-        if self.crypto:
-            self.crypto.setDevice("mapper/%s-%s" % (self.vgname, self.name))
-
-        if not self.isSetup:
-            lvm.lvcreate(self.name, self.vgname, self.size)
-            self.isSetup = 1
-
-            if vgdevice and vgdevice.isNetdev():
-                self.setAsNetdev()
-
-        if self.crypto:
-            self.crypto.formatDevice()
-            self.crypto.openDevice()
-
-        return "/dev/%s" % (self.getDevice(),)
-
-    def getDevice(self, asBoot = 0):
-        if self.crypto and not asBoot:
-            device = self.crypto.getDevice()
-        else:
-            device = "%s/%s" % (self.vgname, self.name)
-
-        return device
-
-    def solidify(self):
-        return
-
-
-class PartitionDevice(Device):
-    def __init__(self, partition, encryption=None):
-        if type(partition) != types.StringType:
-            raise ValueError, "partition must be a string"
-        Device.__init__(self, device=partition, encryption=encryption)
-
-        (disk, pnum) = getDiskPart(partition)
-        if isys.driveIsIscsi(disk):
-            self.setAsNetdev()
-
-    def getDevice(self, asBoot = 0):
-        if self.crypto and not asBoot:
-            return self.crypto.getDevice()
-        else:
-            return self.device
-
-    def setupDevice(self, chroot="/", devPrefix='/dev'):
-        path = '%s/%s' % (devPrefix, self.device)
-        if self.crypto:
-            self.crypto.formatDevice()
-            self.crypto.openDevice()
-            path = "%s/%s" % (devPrefix, self.crypto.getDevice())
-        return path
-
-class PartedPartitionDevice(PartitionDevice):
-    def __init__(self, partition):
-        Device.__init__(self)
-        self.device = None
-        self.partition = partition
-
-    def getDevice(self, asBoot = 0):
-        if not self.partition:
-            return self.device
-
-        return self.partition.getDeviceNodeName()
-
-    def solidify(self):
-        # drop reference on the parted partition object and note
-        # the current minor number allocation
-        self.device = self.getDevice()
-        self.partition = None
-
-class BindMountDevice(Device):
-    def __init__(self, directory):
-        Device.__init__(self)
-        self.device = directory
-
-    def setupDevice(self, chroot="/", devPrefix="/tmp"):
-        return chroot + self.device
-
-class SwapFileDevice(Device):
-    def __init__(self, file):
-        Device.__init__(self)
-        self.device = file
-        self.size = 0
-
-    def setSize (self, size):
-        self.size = size
-
-    def setupDevice (self, chroot="/", devPrefix='/dev'):
-        file = os.path.normpath(chroot + self.getDevice())
-        if not os.access(file, os.R_OK):
-            if self.size:
-                # make sure the permissions are set properly
-                fd = os.open(file, os.O_CREAT, 0600)
-                os.close(fd)
-                isys.ddfile(file, self.size, None)
-            else:
-                raise SystemError, (0, "swap file creation necessary, but "
-                                    "required size is unknown.")
-        return file
-
-# This is a device that describes a swap file that is sitting on
-# the loopback filesystem host for partitionless installs.
-# The piggypath is the place where the loopback file host filesystem
-# will be mounted
-class PiggybackSwapFileDevice(SwapFileDevice):
-    def __init__(self, piggypath, file):
-        SwapFileDevice.__init__(self, file)
-        self.piggypath = piggypath
-
-    def setupDevice(self, chroot="/", devPrefix='/dev'):
-        return SwapFileDevice.setupDevice(self, self.piggypath, devPrefix)
-
-class LoopbackDevice(Device):
-    def __init__(self, hostPartition, hostFs):
-        Device.__init__(self)
-        self.host = "/dev/" + hostPartition
-        self.hostfs = hostFs
-        self.device = "loop1"
-
-    def setupDevice(self, chroot="/", devPrefix='/dev/'):
-        if not self.isSetup:
-            isys.mount(self.host[5:], "/mnt/loophost", fstype = "vfat")
-            self.device = allocateLoopback("/mnt/loophost/redhat.img")
-            if not self.device:
-                raise SystemError, "Unable to allocate loopback device"
-            self.isSetup = 1
-            path = '%s/%s' % (devPrefix, self.getDevice())
-        else:
-            path = '%s/%s' % (devPrefix, self.getDevice())
-        path = os.path.normpath(path)
-        return path
-
-    def getComment (self):
-        return "# LOOP1: %s %s /redhat.img\n" % (self.host, self.hostfs)
-
-def makeDevice(dev):
-    cryptoDev = partitions.lookup_cryptodev(dev)
-    if cryptoDev and cryptoDev.getDevice() == dev:
-        dev = cryptoDev.getDevice(encrypted=True)
-
-    if dev.startswith('md'):
-        try:
-            (mdname, devices, level, numActive) = raid.lookup_raid_device(dev)
-            # convert devices to Device instances and sort out encryption
-            devList = []
-            for dev in devices:
-                cryptoMem = partitions.lookup_cryptodev(dev)
-                if cryptoMem and cryptoMem.getDevice() == dev:
-                    dev = cryptoMem.getDevice(encrypted=True)
-
-                devList.append(PartitionDevice(dev, encryption=cryptoMem))
-
-            device = RAIDDevice(level, devList,
-                                minor=int(mdname[2:]),
-                                spares=len(devices) - numActive,
-                                existing=1, encryption=cryptoDev)
-        except KeyError:
-            device = PartitionDevice(dev, encryption=cryptoDev)
-    else:
-        device = PartitionDevice(dev, encryption=cryptoDev)
-    return device
-
-def findBackingDevInCrypttab(mappingName):
-    backingDev = None
-    try:
-        lines = open("/mnt/sysimage/etc/crypttab").readlines()
-    except IOError, e:
-        pass
-    else:
-        for line in lines:
-            fields = line.split()
-            if len(fields) < 2:
-                continue
-            if fields[0] == mappingName:
-                backingDev = fields[1]
-                break
-
-    return backingDev
-
-# XXX fix RAID
-def readFstab (anaconda):
-    def createMapping(dict):
-        mapping = {}
-        dupes = []
-
-        for device, info in dict.items():
-            if not mapping.has_key(info):
-                mapping[info] = device
-            elif not info in dupes:
-                dupes.append(info)
-
-        return (mapping, dupes)
-
-    def showError(label, intf):
-        if intf:
-            intf.messageWindow(_("Duplicate Labels"),
-                               _("Multiple devices on your system are "
-                                 "labelled %s.  Labels across devices must be "
-                                 "unique for your system to function "
-                                 "properly.\n\n"
-                                 "Please fix this problem and restart the "
-                                 "installation process.") %(label,),
-                               type="custom", custom_icon="error",
-                               custom_buttons=[_("_Exit installer")])
-            sys.exit(0)
-        else:
-            log.warning("Duplicate labels for %s, but no intf so trying "
-                        "to continue" %(label,))
-
-    path = anaconda.rootPath + '/etc/fstab'
-    intf = anaconda.intf
-    fsset = FileSystemSet()
-
-    # first, we look at all the disks on the systems and get any ext2/3
-    # labels off of the filesystem.
-    # temporary, to get the labels
-    diskset = partedUtils.DiskSet(anaconda)
-    diskset.openDevices()
-    labels = diskset.getInfo()
-    uuids = diskset.getInfo(readFn=lambda d: isys.readFSUuid(d))
-
-    (labelToDevice, labelDupes) = createMapping(labels)
-    (uuidToDevice, uuidDupes) = createMapping(uuids)
-
-    loopIndex = {}
-
-    f = open (path, "r")
-    lines = f.readlines ()
-    f.close()
-
-    for line in lines:
-        fields = string.split (line)
-
-        if not fields: continue
-
-        if line[0] == "#":
-            # skip all comments
-            continue
-
-        # all valid fstab entries have 6 fields; if the last two are missing
-        # they are assumed to be zero per fstab(5)
-        if len(fields) < 4:
-            continue
-        elif len(fields) == 4:
-            fields.append(0)
-            fields.append(0)
-        elif len(fields) == 5:
-            fields.append(0)
-        elif len(fields) > 6:
-            continue
-        if string.find(fields[3], "noauto") != -1: continue
-
-        # shenanigans to handle ext3,ext2 format in fstab
-        fstotry = fields[2]
-        if fstotry.find(","):
-            fstotry = fstotry.split(",")
-        else:
-            fstotry = [ fstotry ]
-
-        # ext4 used to be listed as ext4dev, so handle that possibility
-        for i in range(0, len(fstotry)):
-            if fstotry[i] == "ext4dev":
-                fstotry[i] = "ext4"
-
-        fsystem = None
-        for fs in fstotry:
-            # if we don't support mounting the filesystem, continue
-            if not fileSystemTypes.has_key(fs):
-                continue
-            fsystem = fileSystemTypeGet(fs)
-            break
-        # "none" is valid as an fs type for bind mounts (#151458)
-        if fsystem is None and (string.find(fields[3], "bind") == -1):
-            continue
-
-        label = None
-        if fields[0] == "none":
-            device = Device()
-        elif ((string.find(fields[3], "bind") != -1) and
-              fields[0].startswith("/")):
-            # it's a bind mount, they're Weird (tm)
-            device = BindMountDevice(fields[0])
-            fsystem = fileSystemTypeGet("bind")
-        elif len(fields) >= 6 and fields[0].startswith('LABEL='):
-            label = fields[0][6:]
-            if label in labelDupes:
-                showError(label, intf)
-
-            if labelToDevice.has_key(label):
-                device = makeDevice(labelToDevice[label])
-            else:
-                log.warning ("fstab file has LABEL=%s, but this label "
-                             "could not be found on any file system", label)
-                # bad luck, skip this entry.
-                continue
-        elif len(fields) >= 6 and fields[0].startswith('UUID='):
-            uuid = fields[0][5:]
-            if uuid in uuidDupes:
-                showError(uuid, intf)
-
-            if uuidToDevice.has_key(uuid):
-                device = makeDevice(uuidToDevice[uuid])
-            else:
-                log.warning ("fstab file has UUID=%s, but this UUID"
-                             "could not be found on any file system", uuid)
-                # bad luck, skip this entry.
-                continue
-        elif fields[2] == "swap" and not fields[0].startswith('/dev/'):
-            # swap files
-            file = fields[0]
-
-            if file.startswith('/initrd/loopfs/'):
-                file = file[14:]
-                device = PiggybackSwapFileDevice("/mnt/loophost", file)
-            else:
-                device = SwapFileDevice(file)
-        elif fields[0].startswith('/dev/loop'):
-            # look up this loop device in the index to find the
-            # partition that houses the filesystem image
-            # XXX currently we assume /dev/loop1
-            if loopIndex.has_key(device):
-                (dev, fs) = loopIndex[device]
-                device = LoopbackDevice(dev, fs)
-        elif fields[0].startswith("/dev/mapper/luks-"):
-            backingDev = findBackingDevInCrypttab(fields[0][12:])
-            log.debug("device %s has backing device %s" % (fields[0],
-                                                           backingDev))
-            if backingDev is None:
-                log.error("unable to resolve backing device for %s" % fields[0])
-                continue
-            elif backingDev.startswith('LABEL='):
-                label = backingDev[6:]
-                if label in labelDupes:
-                    showError(label, intf)
-
-                if labelToDevice.has_key(label):
-                    device = makeDevice(labelToDevice[label])
-                else:
-                    log.warning ("crypttab file has LABEL=%s, but this label "
-                                 "could not be found on any file system", label)
-                    # bad luck, skip this entry.
-                    continue
-            elif backingDev.startswith('UUID='):
-                uuid = backingDev[5:]
-                if uuid in uuidDupes:
-                    showError(uuid, intf)
-
-                if uuidToDevice.has_key(uuid):
-                    device = makeDevice(uuidToDevice[uuid])
-                else:
-                    log.warning ("crypttab file has UUID=%s, but this UUID"
-                                 "could not be found on any file system", uuid)
-                    # bad luck, skip this entry.
-                    continue
-            else:
-                device = makeDevice(backingDev[5:])
-        elif fields[0].startswith('/dev/'):
-            # Older installs may have lines starting with things like /dev/proc
-            # so watch out for that on upgrade.
-            if fsystem is not None and isinstance(fsystem, PseudoFileSystem):
-                device = Device(device = fields[0][5:])
-            else:
-                device = makeDevice(fields[0][5:])
-        else:
-            device = Device(device = fields[0])
-
-        # if they have a filesystem being mounted as auto, we need
-        # to sniff around a bit to figure out what it might be
-        # if we fail at all, though, just ignore it
-        if fsystem == "auto" and device.getDevice() != "none":
-            try:
-                tmp = isys.readFSType("/dev/%s" %(device.setupDevice(),))
-                if tmp is not None:
-                    fsystem = tmp
-            except:
-                pass
-
-        entry = FileSystemSetEntry(device, fields[1], fsystem, fields[3],
-                                   origfsystem=fsystem)
-        if label:
-            entry.setLabel(label)
-        fsset.add(entry)
-    return fsset
-
-def allocateLoopback(file):
-    found = 1
-    for i in range(8):
-        path = "/dev/loop%d" % (i,)
-        try:
-            isys.losetup(path, file)
-            found = 1
-        except SystemError:
-            continue
-        break
-    if found:
-        return path
-    return None
-
-def ext2FormatFilesystem(argList, messageFile, windowCreator, mntpoint):
-    if windowCreator:
-        w = windowCreator(_("Formatting"),
-                          _("Formatting %s file system...") % (mntpoint,), 100)
-    else:
-        w = None
-
-    fd = os.open(messageFile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
-    p = os.pipe()
-    childpid = os.fork()
-    if not childpid:
-        os.close(p[0])
-        os.dup2(p[1], 1)
-        os.dup2(fd, 2)
-        os.close(p[1])
-        os.close(fd)
-
-        env = os.environ
-        configs = [ "/tmp/updates/mke2fs.conf",
-                    "/etc/mke2fs.conf",
-                  ]
-        for config in configs:
-            if os.access(config, os.R_OK):
-                env['MKE2FS_CONFIG'] = config
-                break
-
-        os.execvpe(argList[0], argList, env)
-        log.critical("failed to exec %s", argList)
-        os._exit(1)
-
-    os.close(p[1])
-
-    # ignoring SIGCHLD would be cleaner then ignoring EINTR, but
-    # we can't use signal() in this thread?
-
-    s = 'a'
-    while s and s != '\b':
-        try:
-            s = os.read(p[0], 1)
-        except OSError, args:
-            (num, str) = args
-            if (num != 4):
-                raise IOError, args
-
-        os.write(fd, s)
-
-    num = ''
-    while s:
-        try:
-            s = os.read(p[0], 1)
-            os.write(fd, s)
-
-            if s != '\b':
-                try:
-                    num = num + s
-                except:
-                    pass
-            else:
-                if num and len(num):
-                    l = string.split(num, '/')
-                    try:
-                        val = (int(l[0]) * 100) / int(l[1])
-                    except (IndexError, TypeError):
-                        pass
-                    else:
-                        w and w.set(val)
-                num = ''
-        except OSError, args:
-            (errno, str) = args
-            if (errno != 4):
-                raise IOError, args
-
-    try:
-        (pid, status) = os.waitpid(childpid, 0)
-    except OSError, (num, msg):
-        log.critical("exception from waitpid while formatting: %s %s" %(num, msg))
-        status = None
-    os.close(fd)
-
-    w and w.pop()
-
-    # *shrug*  no clue why this would happen, but hope that things are fine
-    if status is None:
-        return 0
-
-    if os.WIFEXITED(status) and (os.WEXITSTATUS(status) == 0):
-        return 0
-
-    return 1
-
-# copy and paste job from booty/bootloaderInfo.py...
-def getDiskPart(dev):
-    cut = len(dev)
-    if (dev.startswith('rd/') or dev.startswith('ida/') or
-            dev.startswith('cciss/') or dev.startswith('sx8/') or
-            dev.startswith('mapper/') or dev.startswith('mmcblk')):
-        if dev[-2] == 'p':
-            cut = -1
-        elif dev[-3] == 'p':
-            cut = -2
-    else:
-        if dev[-2] in string.digits:
-            cut = -2
-        elif dev[-1] in string.digits:
-            cut = -1
-
-    name = dev[:cut]
-
-    # hack off the trailing 'p' from /dev/cciss/*, for example
-    if name[-1] == 'p':
-        for letter in name:
-            if letter not in string.letters and letter != "/":
-                name = name[:-1]
-                break
-
-    if cut < 0:
-        partNum = int(dev[cut:]) - 1
-    else:
-        partNum = None
-
-    return (name, partNum)
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/gui.py anaconda-storage/gui.py
--- anaconda/gui.py	2009-02-13 23:03:10.000000000 -0600
+++ anaconda-storage/gui.py	2009-02-15 15:04:24.000000000 -0600
@@ -752,7 +752,7 @@
         elif len(dests) > 0:
             for d in dests:
                 iter = store.append(None)
-                store[iter] = ("/dev/%s" % d[0], "/dev/%s - %s" % (d[0], d[1]))
+                store[iter] = (d[0], "%s - %s" % (d[0], d[1]))
 
             self.diskCombo.set_model(store)
             self.diskCombo.set_active(0)
@@ -1267,10 +1267,6 @@
         if anaconda.id.keyboard and not anaconda.id.x_already_set:
             anaconda.id.keyboard.activate()
 
-        anaconda.id.fsset.registerMessageWindow(self.messageWindow)
-        anaconda.id.fsset.registerProgressWindow(self.progressWindow)
-        anaconda.id.fsset.registerWaitWindow(self.waitWindow)
-
         self.icw = InstallControlWindow (self.anaconda)
         self.icw.run (self.runres)
 
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/installclass.py anaconda-storage/installclass.py
--- anaconda/installclass.py	2009-02-13 23:03:10.000000000 -0600
+++ anaconda-storage/installclass.py	2009-02-15 15:14:18.000000000 -0600
@@ -188,11 +188,11 @@
         from backend import AnacondaBackend
         return AnacondaBackend
 
-    def setDefaultPartitioning(self, partitions, clear = CLEARPART_TYPE_LINUX,
+    def setDefaultPartitioning(self, storage, clear = CLEARPART_TYPE_LINUX,
                                doClear = 1, useLVM = True):
         autorequests = [ ("/", None, 1024, None, 1, 1, 1) ]
 
-        bootreq = getAutopartitionBoot(partitions)
+        bootreq = getAutopartitionBoot(storage)
         if bootreq:
             autorequests.extend(bootreq)
 
@@ -200,13 +200,13 @@
         autorequests.append((None, "swap", minswap, maxswap, 1, 1, 1))
 
         if doClear:
-            partitions.autoClearPartType = clear
-            partitions.autoClearPartDrives = []
+            storage.autoClearPartType = clear
+            storage.autoClearPartDrives = []
 
         if useLVM:
-            partitions.autoPartitionRequests = autoCreateLVMPartitionRequests(autorequests)
+            storage.autoPartitionRequests = autoCreateLVMPartitionRequests(autorequests)
         else:
-            partitions.autoPartitionRequests = autoCreatePartitionRequests(autorequests)        
+            storage.autoPartitionRequests = autoCreatePartitionRequests(autorequests)        
 
 
     def setInstallData(self, anaconda):
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/installmethod.py anaconda-storage/installmethod.py
--- anaconda/installmethod.py	2009-02-11 21:36:30.000000000 -0600
+++ anaconda-storage/installmethod.py	2009-02-15 15:17:18.000000000 -0600
@@ -47,9 +47,9 @@
         isys.ejectCdrom(dev)
 
     mtab = "/dev/root / ext3 ro 0 0\n"
-    for ent in anaconda.id.fsset.entries:
-        if ent.mountpoint == "/":
-            mtab = "/dev/root / %s ro 0 0\n" %(ent.fsystem.name,)
+    rootDevice = anaconda.id.storage.rootDevice
+    if rootDevice:
+        mtab = "/dev/root / %s ro 0 0\n" % rootDevice.format.type
 
     f = open(anaconda.rootPath + "/etc/mtab", "w+")
     f.write(mtab)
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/instdata.py anaconda-storage/instdata.py
--- anaconda/instdata.py	2009-02-13 23:02:48.000000000 -0600
+++ anaconda-storage/instdata.py	2009-02-17 13:15:06.000000000 -0600
@@ -30,10 +30,8 @@
 import security
 import timezone
 import desktop
-import fsset
+import devicetree
 import bootloader
-import partitions
-import partedUtils
 import iscsi
 import zfcp
 import urllib
@@ -67,8 +65,11 @@
 
         self.instClass = None
         self.network = network.Network()
+
+        # can we move these into self.storage?
         self.iscsi = iscsi.iscsi()
         self.zfcp = zfcp.ZFCP()
+
         self.firewall = firewall.Firewall()
         self.security = security.Security()
         self.timezone = timezone.Timezone()
@@ -80,10 +81,7 @@
         self.upgrade = None
         if flags.cmdline.has_key("preupgrade"):
             self.upgrade = True
-        # XXX move fsset and/or diskset into Partitions object?
-        self.fsset.reset()
-        self.diskset = partedUtils.DiskSet(self.anaconda)
-        self.partitions = partitions.Partitions(self.anaconda)
+        self.storage = storage.Storage(self.anaconda)
         self.bootloader = bootloader.getBootloader()
         self.upgradeRoot = None
         self.rootParts = None
@@ -102,19 +100,20 @@
         if os.path.exists("/dev/live") and \
            stat.S_ISBLK(os.stat("/dev/live")[stat.ST_MODE]):
             target = os.readlink("/dev/live")
-            self.partitions.protected = [target]
+            self.storage.protectedPartitions = [target]
         elif self.anaconda.methodstr and self.anaconda.methodstr.startswith("hd:"):
+            # FIXME: look up this device using the device tree
             method = self.anaconda.methodstr[3:]
-            device = method.split(":", 3)[0]
+            devspec = method.split(":", 3)[0]
+            
+            device = storage.resolveDevice(devspec)
+            if not device:
+                try:
+                    device = self.devicetree.getDeviceByName(devspec)
+                except AttributeError:
+                    device = None
 
-            if device.startswith("LABEL="):
-                dev = isys.getDeviceByToken("LABEL", device[6:])
-            elif device.startswith("UUID="):
-                dev = isys.getDeviceByToken("UUID", device[5:])
-            else:
-                dev = device
-
-            if dev is None:
+            if device is None:
                 if self.getUpgrade():
                     return
                 else:
@@ -125,10 +124,7 @@
                         type="custom", custom_buttons = [_("_Exit installer")])
                     sys.exit(1)
 
-            if dev.startswith("/dev/"):
-                dev = dev[5:]
-
-            self.partitions.protected = [dev]
+            self.storage.protectedPartitions = [device.name]
 
     def setInstallProgressClass(self, c):
         self.instProgress = c
@@ -312,7 +308,7 @@
         self.security.writeKS(f)
         self.timezone.writeKS(f)
         self.bootloader.writeKS(f)
-        self.partitions.writeKS(f)
+        self.storage.writeKS(f)
 
         if self.backend is not None:
             self.backend.writeKS(f)
@@ -339,6 +335,5 @@
         self.videocard = None
         self.isHeadless = 0
         self.extraModules = extraModules
-        self.fsset = fsset.FileSystemSet()
 
         self.reset()
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/iscsi.py anaconda-storage/iscsi.py
--- anaconda/iscsi.py	2009-02-13 23:03:10.000000000 -0600
+++ anaconda-storage/iscsi.py	2009-02-17 12:45:03.000000000 -0600
@@ -297,22 +297,13 @@
 
         if not flags.test:
             root_drives = [ ]
-            req = anaconda.id.partitions.getRequestByMountPoint("/")
-            root_requests = anaconda.id.partitions.getUnderlyingRequests(req)
-            for req in root_requests:
-                for drive in req.drive:
-                    part = anaconda.id.diskset.disks[drive].getPartitionByPath("/dev/%s" % req.device)
-                    if part:
-                        break
-                if not part:
-                    continue
-                if drive not in root_drives:
-                    root_drives.append(drive)
-
+            root = anaconda.id.storage.fsset.rootDevice
+            root_deps = anaconda.id.storage.devicetree.getDependentDevices(root)
+            root_drives = [d.path for d in root_deps if d.type == "disk"]
             log.debug("iscsi.write: root_drives: %s" % (string.join(root_drives),))
 
             # set iscsi nodes not used for root to autostart
-            for disk in anaconda.id.diskset.disks.keys():
+            for disk in [d.path for d in anaconda.id.storage.disks]:
                 if isys.driveIsIscsi(disk) and not disk in root_drives:
                     iscsi_make_node_autostart(disk)
 
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/iw/partition_dialog_gui.py anaconda-storage/iw/partition_dialog_gui.py
--- anaconda/iw/partition_dialog_gui.py	2009-02-13 23:03:10.000000000 -0600
+++ anaconda-storage/iw/partition_dialog_gui.py	2009-02-17 13:16:21.000000000 -0600
@@ -26,9 +26,7 @@
 import gtk
 
 import gui
-from fsset import *
-from cryptodev import LUKSDevice
-from partRequests import *
+from storage.devices import PartitionDevice
 from partition_ui_helpers_gui import *
 from constants import *
 
@@ -50,13 +48,6 @@
 	adj.clamp_page(size, adj.upper)
 	fillmaxszsb.set_adjustment(adj)
 
-    def cylspinchangedCB(self, widget, data):
-	(dev, startcylspin, endcylspin, bycyl_sizelabel) = data
-	startsec = dev.startCylinderToSector(startcylspin.get_value_as_int())
-	endsec = dev.endCylinderToSector(endcylspin.get_value_as_int())
-	cursize = (endsec - startsec)/2048
-	bycyl_sizelabel.set_text("%s" % (int(cursize))) 
-
     def fillmaxszCB(self, widget, spin):
 	spin.set_sensitive(widget.get_active())
 
@@ -111,150 +102,140 @@
 
         while 1:
             rc = self.dialog.run()
+            actions = []
 	    
             # user hit cancel, do nothing
             if rc == 2:
                 self.destroy()
                 return None
 
-            if self.origrequest.type == REQUEST_NEW:
+            if not self.origrequest.exists:
                 # read out UI into a partition specification
-                filesystem = self.newfstypeCombo.get_active_value()
-
-                request = copy.copy(self.origrequest)
-                request.fstype = filesystem
-                request.format = True
-
-                if request.fstype.isMountable():
-                    request.mountpoint = self.mountCombo.get_children()[0].get_text()
-                else:
-                    request.mountpoint = None
+                fmt_class = self.newfstypeCombo.get_active_value()
+                # there's nothing about origrequest we care about
+                #request = copy.copy(self.origrequest)
+                mountpoint = self.mountCombo.get_children()[0].get_text()
 
                 if self.primonlycheckbutton.get_active():
-                    primonly = True
+                    primary = True
                 else:
-                    primonly = None
+                    primary = None
 
+                format = fmt_class(mountpoint=mountpoint)
                 if self.lukscb and self.lukscb.get_active():
-                    if not request.encryption:
-                        request.encryption = LUKSDevice(passphrase = self.partitions.encryptionPassphrase, format=1)
+                    luksformat = format
+                    format = getFormat("luks",
+                                       passphrase=self.storage.encryptionPassphrase)
+                    luksdev = LUKSDevice("luks%d" % self.storage.nextID,
+                                         format=format,
+                                         parents=request)
+
+                if self.fixedrb.get_active():
+                    grow = None
                 else:
-                    request.encryption = None
+                    grow = True
+
+                self.sizespin.update()
 
-                if not self.newbycyl:
-                    if self.fixedrb.get_active():
-                        grow = None
-                    else:
-                        grow = True
-
-                    self.sizespin.update()
-
-                    if self.fillmaxszrb.get_active():
-                        self.fillmaxszsb.update()
-                        maxsize = self.fillmaxszsb.get_value_as_int()
-                    else:
-                        maxsize = None
-
-		    allowdrives = []
-		    model = self.driveview.get_model()
-		    iter = model.get_iter_first()
-		    while iter:
-			val   = model.get_value(iter, 0)
-			drive = model.get_value(iter, 1)
-
-			if val:
-			    allowdrives.append(drive)
-
-                        iter = model.iter_next(iter)
-
-                    if len(allowdrives) == len(self.diskset.disks.keys()):
-                        allowdrives = None
-			
-                    request.size = self.sizespin.get_value_as_int()
-                    request.drive = allowdrives
-                    request.grow = grow
-                    request.primary = primonly
-                    request.maxSizeMB = maxsize
+                if self.fillmaxszrb.get_active():
+                    self.fillmaxszsb.update()
+                    maxsize = self.fillmaxszsb.get_value_as_int()
                 else:
-                    self.startcylspin.update()
-                    self.endcylspin.update()
-                    
-                    request.start = self.startcylspin.get_value_as_int()
-                    request.end = self.endcylspin.get_value_as_int()
-                    request.primary = primonly
-
-                    if request.end <= request.start:
-                        self.intf.messageWindow(_("Error With Request"),
-                                                _("The end cylinder must be "
-                                                "greater than the start "
-                                                "cylinder."), custom_icon="error")
+                    maxsize = None
 
-                        continue
+                allowdrives = []
+                model = self.driveview.get_model()
+                iter = model.get_iter_first()
+                while iter:
+                    val   = model.get_value(iter, 0)
+                    drive = model.get_value(iter, 1)
+
+                    if val:
+                        allowdrives.append(drive)
+
+                    iter = model.iter_next(iter)
+
+                if len(allowdrives) == len(self.storage.disks):
+                    allowdrives = None
+
+                size = self.sizespin.get_value_as_int()
+                disks = []
+                if allowdrives:
+                    for drive in allowdrives:
+                        for disk in self.storage.disks:
+                            if disk.name == drive:
+                                disks.append(disk)
+
+                # TODO: when will we set bootable?
+                request = PartitionDevice("new%d" % self.storage.nextID,
+                                          format=format,
+                                          size=size,
+                                          grow=grow,
+                                          maxsize=maxsize,
+                                          primary=primary,
+                                          parents=disks)
 
-		err = request.sanityCheckRequest(self.partitions)
+		err = storage.sanityCheckRequest(request)
 		if not err:
-		    err = doUIRAIDLVMChecks(request, self.diskset)
+		    err = doUIRAIDLVMChecks(request, self.storage)
 		    
                 if err:
                     self.intf.messageWindow(_("Error With Request"),
                                             "%s" % (err), custom_icon="error")
                     continue
+
+                # we're all set, so create the actions
+                if luksdev:
+                    actions.append(ActionCreateDevice(luksdev))
+                actions.append(ActionCreateDevice(request))
             else:
                 # preexisting partition, just set mount point and format flag
                 request = copy.copy(self.origrequest)
-                request.encryption = copy.deepcopy(self.origrequest.encryption)
-		
-		if self.fsoptionsDict.has_key("formatcb"):
-                    request.format = self.fsoptionsDict["formatcb"].get_active()
-                    if request.format:
-                        request.fstype = self.fsoptionsDict["fstypeCombo"].get_active_value()
-                else:
-                    request.format = 0
-
-		if self.fsoptionsDict.has_key("migratecb"):
-                    request.migrate = self.fsoptionsDict["migratecb"].get_active()
-                    if request.migrate:
-                        request.fstype =self.fsoptionsDict["migfstypeCombo"].get_active_value()
-                else:
-                    request.migrate = 0
-
-                if self.fsoptionsDict.has_key("resizecb") and self.fsoptionsDict["resizecb"].get_active():
-                    request.targetSize = self.fsoptionsDict["resizesb"].get_value_as_int()
-                else:
-                    request.targetSize = None
-
-                # set back if we are not formatting or migrating
-		origfstype = self.origrequest.origfstype
-                if not request.format and not request.migrate:
-                    request.fstype = origfstype
-
-                if request.fstype.isMountable():
-                    request.mountpoint =  self.mountCombo.get_children()[0].get_text()
-                else:
-                    request.mountpoint = None
+                mountpoint = self.mountCombo.get_children()[0].get_text()
+		if self.fsoptionsDict.has_key("formatcb") and \
+                   self.fsoptionsDict["formatcb"].get_active():
+                    fmt_class = self.fsoptionsDict["fstypeCombo"].get_active_value()
+                    format = fmt_class(mountpoint=mountpoint)
+                    if self.fsoptionsDict.has_key("lukscb") and \
+                       lukscb.get_active() and \
+                       request.format.type != "luks":
+                        luksdev = LUKSDevice("luks%d" % self.storage.nextID,
+                                             format=format,
+                                             parents=request)
+                        actions.append(ActionCreateDevice(luksdev)
+                        format = getFormat("luks",
+                                           passphrase=self.storage.encryptionPassphrase)
+                    actions.append(ActionCreateFormat(request, format))
+
+		if self.fsoptionsDict.has_key("migratecb") and \
+                   self.fsoptionsDict["migratecb"].get_active():
+                    format = getFormat(self.fsoptionsDict["migfstypeCombo"].get_active_value(), mountpoint=mountpoint)
+                    # TODO: implement ActionMigrateFormat
+                    #actions.append(ActionMigrateFormat(request, format))
+
+                if self.fsoptionsDict.has_key("resizecb") and \
+                   self.fsoptionsDict["resizecb"].get_active():
+                    size = self.fsoptionsDict["resizesb"].get_value_as_int()
+                    actions.append(ActionResizeDevice(request, size)
+                    if request.format.type != "none":
+                        actions.append(ActionResizeFormat(request, size))
 
-                lukscb = self.fsoptionsDict.get("lukscb")
-                if lukscb and lukscb.get_active():
-                    if not request.encryption:
-                        request.encryption = LUKSDevice(passphrase=self.partitions.encryptionPassphrase, format=1)
-                else:
-                    request.encryption = None
-
-                err = request.sanityCheckRequest(self.partitions)
+                err = self.storage.sanityCheckRequest(request)
                 if err:
                     self.intf.messageWindow(_("Error With Request"),
                                             "%s" % (err), custom_icon="error")
                     continue
 
-                if (not request.format and
-                    request.mountpoint and request.formatByDefault()):
+                if request.format.exists and \
+                   getattr(request, "mountpoint", None) and \
+                   self.storage.formatByDefault(request)):
                     if not queryNoFormatPreExisting(self.intf):
                         continue
-		    
-	    # everything ok, fall out of loop
+
+    	    # everything ok, fall out of loop
 	    break
-            
-	return request
+
+	return actions
 
     def destroy(self):
 	if self.dialog:
@@ -265,8 +246,7 @@
     def __init__(self, anaconda, parent, origrequest, isNew = 0,
                  restrictfs = None):
         self.anaconda = anaconda
-	self.partitions = self.anaconda.id.partitions
-	self.diskset = self.anaconda.id.diskset
+	self.storage = self.anaconda.id.storage
 	self.intf = self.anaconda.intf
 	self.origrequest = origrequest
 	self.isNew = isNew
@@ -275,10 +255,7 @@
 	if isNew:
 	    tstr = _("Add Partition")
 	else:
-	    try:
-		tstr = _("Edit Partition: /dev/%s") % (origrequest.device,)
-	    except:
-		tstr = _("Edit Partition")
+	    tstr = _("Edit Partition: %s") % (origrequest.path,)
 	    
         self.dialog = gtk.Dialog(tstr, self.parent)
         gui.addFrame(self.dialog)
@@ -291,24 +268,36 @@
         maintable.set_col_spacings(5)
         row = 0
 
-        # see if we are creating a floating request or by cylinder
-        if self.origrequest.type == REQUEST_NEW:
-            self.newbycyl = self.origrequest.start != None
+        # if this is a luks device we need to grab info from two devices
+        # to make it seem like one device. wee!
+        if self.origrequest.format.type == "luks":
+            luksdev = self.storage.devicetree.getChildren(self.origrequest)[0]
+            fmt_type = luksdev.format.type
+            mountpoint = getattr(luksdev.format, "mountpoint", None)
+            fslabel = getattr(luksdev.format, "label", None)
+            # XXX might need to add migrate stuff here, too
+            usereq = luksdev
+        else:
+            usereq = self.origrequest
 
         # Mount Point entry
 	lbl = createAlignedLabel(_("_Mount Point:"))
         maintable.attach(lbl, 0, 1, row, row + 1)
-        self.mountCombo = createMountPointCombo(origrequest)
+        self.mountCombo = createMountPointCombo(usereq)
 	lbl.set_mnemonic_widget(self.mountCombo)
         maintable.attach(self.mountCombo, 1, 2, row, row + 1)
         row = row + 1
 
         # Partition Type
-        if self.origrequest.type == REQUEST_NEW:
+        if not self.origrequest.format.exists:
 	    lbl = createAlignedLabel(_("File System _Type:"))
             maintable.attach(lbl, 0, 1, row, row + 1)
 
-            self.newfstypeCombo = createFSTypeMenu(self.origrequest.fstype,
+            if luksdev:
+                usereq = luksdev
+            else:
+                usereq = self.origrequest
+            self.newfstypeCombo = createFSTypeMenu(usereq.format.type,
                                                    fstypechangeCB,
                                                    self.mountCombo,
                                                    availablefstypes = restrictfs)
@@ -320,103 +309,49 @@
         row = row + 1
 
         # allowable drives
-        if self.origrequest.type == REQUEST_NEW:
-            if not self.newbycyl:
-		lbl = createAlignedLabel(_("Allowable _Drives:"))
-                maintable.attach(lbl, 0, 1, row, row + 1)
-
-                self.driveview = createAllowedDrivesList(self.diskset.disks,
-                                                         self.origrequest.drive,
-                                                         selectDrives=False,
-                                                         disallowDrives=[self.anaconda.updateSrc])
-		lbl.set_mnemonic_widget(self.driveview)
-                sw = gtk.ScrolledWindow()
-                sw.add(self.driveview)
-                sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
-		sw.set_shadow_type(gtk.SHADOW_IN)
-                maintable.attach(sw, 1, 2, row, row + 1)
-		self.driveview.set_size_request(375, 80)
-            else:
-                maintable.attach(createAlignedLabel(_("Drive:")),
-                                 0, 1, row, row + 1)
-                maintable.attach(createAlignedLabel(origrequest.drive[0]),
-                                 1, 2, row, row + 1)
+        if not self.origrequest.exists:
+            lbl = createAlignedLabel(_("Allowable _Drives:"))
+            maintable.attach(lbl, 0, 1, row, row + 1)
+
+            self.driveview = createAllowedDrivesList(self.storage.disks,
+                                                     self.origrequest.req_disks,
+                                                     selectDrives=False,
+                                                     disallowDrives=[self.anaconda.updateSrc])
+            lbl.set_mnemonic_widget(self.driveview)
+            sw = gtk.ScrolledWindow()
+            sw.add(self.driveview)
+            sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+            sw.set_shadow_type(gtk.SHADOW_IN)
+            maintable.attach(sw, 1, 2, row, row + 1)
+            self.driveview.set_size_request(375, 80)
 
             row = row + 1
 
         # original fs label
-        if self.origrequest.type != REQUEST_NEW and self.origrequest.fslabel:
+        if usereq.format.exists and \ 
+           getattr(usereq.format, "label", None):
             maintable.attach(createAlignedLabel(_("Original File System "
                                                   "Label:")),
                              0, 1, row, row + 1)
-            fslabel = gtk.Label(self.origrequest.fslabel)
+            fslabel = gtk.Label(usereq.format.label)
             maintable.attach(fslabel, 1, 2, row, row + 1)
-
             row = row + 1
 
         # size
-        if self.origrequest.type == REQUEST_NEW:
-            if not self.newbycyl:
-                # Size specification
-		lbl = createAlignedLabel(_("_Size (MB):"))
-                maintable.attach(lbl, 0, 1, row, row + 1)
-                sizeAdj = gtk.Adjustment(value = 1, lower = 1,
-                                         upper = MAX_PART_SIZE, step_incr = 1)
-                self.sizespin = gtk.SpinButton(sizeAdj, digits = 0)
-                self.sizespin.set_property('numeric', True)
-
-                if self.origrequest.size:
-                    self.sizespin.set_value(self.origrequest.size)
-
-		lbl.set_mnemonic_widget(self.sizespin)
-                maintable.attach(self.sizespin, 1, 2, row, row + 1)
-                bycyl_sizelabel = None
-            else:
-                # XXX need to add partition by size and
-                #     wire in limits between start and end
-                dev = self.diskset.disks[origrequest.drive[0]].dev
-                maintable.attach(createAlignedLabel(_("Size (MB):")),
-                                 0, 1, row, row + 1)
-                bycyl_sizelabel = createAlignedLabel("")
-                maintable.attach(bycyl_sizelabel, 1, 2, row, row + 1)
-                row = row + 1
+        if not self.origrequest.exists:
+            # Size specification
+            lbl = createAlignedLabel(_("_Size (MB):"))
+            maintable.attach(lbl, 0, 1, row, row + 1)
+            sizeAdj = gtk.Adjustment(value = 1, lower = 1,
+                                     upper = MAX_PART_SIZE, step_incr = 1)
+            self.sizespin = gtk.SpinButton(sizeAdj, digits = 0)
+            self.sizespin.set_property('numeric', True)
 
-		lbl = createAlignedLabel(_("_Start Cylinder:"))
-                maintable.attach(lbl, 0, 1, row, row + 1)
+            if self.origrequest.req_size:
+                self.sizespin.set_value(self.origrequest.req_size)
 
-                (maxcyl, h, s) = self.diskset.disks[origrequest.drive[0]].device.biosGeometry
-                cylAdj = gtk.Adjustment(value=origrequest.start,
-                                        lower=origrequest.start,
-                                        upper=maxcyl,
-                                        step_incr=1)
-                self.startcylspin = gtk.SpinButton(cylAdj, digits=0)
-                self.startcylspin.set_property('numeric', True)
-		lbl.set_mnemonic_widget(self.startcylspin)
-                maintable.attach(self.startcylspin, 1, 2, row, row + 1)
-                row = row + 1
-                
-                endcylAdj = gtk.Adjustment(value=origrequest.end,
-                                           lower=origrequest.start,
-                                           upper=maxcyl,
-                                           step_incr=1)
-		lbl = createAlignedLabel(_("_End Cylinder:"))		
-                maintable.attach(lbl, 0, 1, row, row + 1)
-                self.endcylspin = gtk.SpinButton(endcylAdj, digits = 0)
-                self.endcylspin.set_property('numeric', True)
-		lbl.set_mnemonic_widget(self.endcylspin)
-                maintable.attach(self.endcylspin, 1, 2, row, row + 1)
-
-                self.startcylspin.connect("value-changed", self.cylspinchangedCB,
-					  (dev, self.startcylspin,
-					   self.endcylspin, bycyl_sizelabel))
-                self.endcylspin.connect("value-changed", self.cylspinchangedCB,
-					(dev, self.startcylspin,
-					 self.endcylspin, bycyl_sizelabel))
-                
-                startsec = dev.startCylinderToSector(origrequest.start)
-                endsec = dev.endCylinderToSector(origrequest.end)
-                cursize = (endsec - startsec)/2048
-                bycyl_sizelabel.set_text("%s" % (int(cursize)))
+            lbl.set_mnemonic_widget(self.sizespin)
+            maintable.attach(self.sizespin, 1, 2, row, row + 1)
         else:
             self.sizespin = None
             
@@ -425,45 +360,43 @@
         # format/migrate options for pre-existing partitions, as long as they
         # aren't protected (we'd still like to be able to mount them, though)
 	self.fsoptionsDict = {}
-        if self.origrequest.type == REQUEST_PREEXIST and self.origrequest.fstype and not self.origrequest.getProtected():
-	    (row, self.fsoptionsDict) = createPreExistFSOptionSection(self.origrequest, maintable, row, self.mountCombo, self.partitions)
+        if self.origrequest.exists and \
+           usereq.format.type != "none" and \
+           not self.storage.isProtected(self.origrequest):
+	    (row, self.fsoptionsDict) = createPreExistFSOptionSection(usereq, maintable, row, self.mountCombo, self.storage)
 
         # size options
-        if self.origrequest.type == REQUEST_NEW:
-            if not self.newbycyl:
-                (sizeframe, self.fixedrb, self.fillmaxszrb,
-                 self.fillmaxszsb) = self.createSizeOptionsFrame(self.origrequest,
-							    self.fillmaxszCB)
-                self.sizespin.connect("value-changed", self.sizespinchangedCB,
-				      self.fillmaxszsb)
+        if not self.origrequest.exists:
+            (sizeframe, self.fixedrb, self.fillmaxszrb,
+             self.fillmaxszsb) = self.createSizeOptionsFrame(self.origrequest,
+                                                        self.fillmaxszCB)
+            self.sizespin.connect("value-changed", self.sizespinchangedCB,
+                                  self.fillmaxszsb)
 
-                maintable.attach(sizeframe, 0, 2, row, row + 1)
-            else:
-                # XXX need new by cyl options (if any)
-                pass
+            maintable.attach(sizeframe, 0, 2, row, row + 1)
             row = row + 1
         else:
             self.sizeoptiontable = None
 
         # create only as primary
-        if self.origrequest.type == REQUEST_NEW:
+        if not self.origrequest.exists:
             self.primonlycheckbutton = gtk.CheckButton(_("Force to be a _primary "
                                                     "partition"))
             self.primonlycheckbutton.set_active(0)
-            if self.origrequest.primary:
+            if self.origrequest.req_primary:
                 self.primonlycheckbutton.set_active(1)
 
             # only show if we have something other than primary
-            if not self.diskset.onlyPrimaryParts():
+            if self.storage.extendedPartitionsSupported():
                 maintable.attach(self.primonlycheckbutton, 0, 2, row, row+1)
                 row = row + 1
 
         # checkbutton for encryption using dm-crypt/LUKS
-        if self.origrequest.type == REQUEST_NEW:
+        if not self.origrequest.exists:
             self.lukscb = gtk.CheckButton(_("_Encrypt"))
             self.lukscb.set_data("formatstate", 1)
 
-            if self.origrequest.encryption:
+            if self.origrequest.format.type == "luks":
                 self.lukscb.set_active(1)
             else:
                 self.lukscb.set_active(0)
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/iw/partition_gui.py anaconda-storage/iw/partition_gui.py
--- anaconda/iw/partition_gui.py	2009-02-13 23:03:10.000000000 -0600
+++ anaconda-storage/iw/partition_gui.py	2009-02-17 12:02:31.000000000 -0600
@@ -372,8 +372,8 @@
 #              (N_("Size (MB)"), gobject.TYPE_STRING, 1.0, 0, isLeaf),
               (N_("Format"), gobject.TYPE_OBJECT, 0.5, 0, isFormattable),
               (N_("Size (MB)"), gobject.TYPE_STRING, 1.0, 0, 0),
-              (N_("Start"), gobject.TYPE_STRING, 1.0, 0, 1),
-              (N_("End"), gobject.TYPE_STRING, 1.0, 0, 1),
+#              (N_("Start"), gobject.TYPE_STRING, 1.0, 0, 1),
+#              (N_("End"), gobject.TYPE_STRING, 1.0, 0, 1),
 	      ("", gobject.TYPE_STRING, 0.0, 0, 0),
               # the following must be the last two
               ("IsLeaf", gobject.TYPE_BOOLEAN, 0.0, 1, 0),
@@ -632,8 +632,7 @@
         return rc
         
     def getNext(self):
-        (errors, warnings) = self.partitions.sanityCheckAllRequests(self.diskset)
-
+        (errors, warnings) = self.storage.sanityCheckAllRequests()
         if errors:
             labelstr1 =  _("The partitioning scheme you requested "
                            "caused the following critical errors.")
@@ -649,6 +648,7 @@
             raise gui.StayOnScreen
         
         if warnings:
+            # "storage configuration"
             labelstr1 = _("The partitioning scheme you requested "
                           "generated the following warnings.")
             labelstr2 = _("Would you like to continue with "
@@ -663,8 +663,7 @@
             if rc != 1:
                 raise gui.StayOnScreen
 
-        formatWarnings = getPreExistFormatWarnings(self.partitions,
-                                                   self.diskset)
+        formatWarnings = getPreExistFormatWarnings(self.storage)
         if formatWarnings:
             labelstr1 = _("The following pre-existing partitions have been "
                           "selected to be formatted, destroying all data.")
@@ -676,7 +675,7 @@
             commentstr = ""
             for (dev, type, mntpt) in formatWarnings:
                 commentstr = commentstr + \
-                        "/dev/%s         %s         %s\n" % (dev,type,mntpt)
+                        "%s         %s         %s\n" % (dev,type,mntpt)
 
             rc = self.presentPartitioningComments(_("Format Warnings"),
                                                   labelstr1, labelstr2,
@@ -695,161 +694,153 @@
 
     def getPrev(self):
         self.diskStripeGraph.shutDown()
-        self.diskset.refreshDevices()
-        self.partitions.setFromDisk(self.diskset)
+        self.storage.reset()
         self.tree.clear()
         del self.parent
         return None
 
-    def getShortFSTypeName(self, name):
-	if name == "physical volume (LVM)":
-	    return "LVM PV"
-
-	return name
-    
     def populate(self, initial = 0):
-
-        drives = self.diskset.disks.keys()
-        drives.sort()
-
         self.tree.resetSelection()
 
 	self.tree.clearHiddenPartitionsList()
 
 	# first do LVM
-        lvmrequests = self.partitions.getLVMRequests()
-        if lvmrequests:
+        vgs = self.storage.vgs
+        if vgs:
 	    lvmparent = self.tree.append(None)
 	    self.tree[lvmparent]['Device'] = _("LVM Volume Groups")
-            for vgname in lvmrequests.keys():
-		vgrequest = self.partitions.getRequestByVolumeGroupName(vgname)
-		rsize = vgrequest.getActualSize(self.partitions, self.diskset)
+            for vg in vgs:
+		rsize = vg.size
 
                 vgparent = self.tree.append(lvmparent)
-		self.tree[vgparent]['Device'] = "%s" % (vgname,)
-                if vgrequest and vgrequest.type != REQUEST_NEW and vgrequest.fslabel:
-                    self.tree[vgparent]['Label'] = "%s" % (vgrequest.fslabel,)
-                else:
-                    self.tree[vgparent]['Label'] = ""
+		self.tree[vgparent]['Device'] = "%s" % vg.name
+                self.tree[vgparent]['Label'] = ""
 		self.tree[vgparent]['Mount Point'] = ""
-		self.tree[vgparent]['Start'] = ""
-		self.tree[vgparent]['End'] = ""
+		#self.tree[vgparent]['Start'] = ""
+		#self.tree[vgparent]['End'] = ""
 		self.tree[vgparent]['Size (MB)'] = "%Ld" % (rsize,)
-                self.tree[vgparent]['PyObject'] = str(vgrequest.uniqueID)
-		for lvrequest in lvmrequests[vgname]:
+                #self.tree[vgparent]['PyObject'] = str(vgrequest.uniqueID)
+		for lv in vg.lvs:
 		    iter = self.tree.append(vgparent)
-		    self.tree[iter]['Device'] = lvrequest.logicalVolumeName
-		    if lvrequest.fstype and lvrequest.mountpoint:
-			self.tree[iter]['Mount Point'] = lvrequest.mountpoint
+		    self.tree[iter]['Device'] = lv.lvname
+		    if lv.format.mountable and lv.format.mountpoint:
+			self.tree[iter]['Mount Point'] = lv.format.mountpoint
 		    else:
 			self.tree[iter]['Mount Point'] = ""
-		    self.tree[iter]['Size (MB)'] = "%Ld" % (lvrequest.getActualSize(self.partitions, self.diskset, True),)
+		    self.tree[iter]['Size (MB)'] = "%Ld" % lv.size
+                    # FIXME: need something to store as unique id
 		    self.tree[iter]['PyObject'] = str(lvrequest.uniqueID)
 		
-                    ptype = lvrequest.fstype.getName()
-                    if lvrequest.isEncrypted(self.partitions, True) and lvrequest.format:
+                    if lv.format == "luks" and not lv.format.exists:
 			self.tree[iter]['Format'] = self.lock_pixbuf
-                    elif lvrequest.format:
+                    elif not lv.format.exists:
 			self.tree[iter]['Format'] = self.checkmark_pixbuf
-                    self.tree[iter]['IsFormattable'] = lvrequest.fstype.isFormattable()
+                    self.tree[iter]['IsFormattable'] = lv.format.formattable
 		    self.tree[iter]['IsLeaf'] = True
-		    self.tree[iter]['Type'] = ptype
-		    self.tree[iter]['Start'] = ""
-		    self.tree[iter]['End'] = ""
+                    # FIXME: DevicFormat needs an attr to store a pretty
+                    #        type name
+		    self.tree[iter]['Type'] = lv.format.type
+		    #self.tree[iter]['Start'] = ""
+		    #self.tree[iter]['End'] = ""
 
         # handle RAID next
-        raidrequests = self.partitions.getRaidRequests()
-        if raidrequests:
+        mdarrays = self.storage.mdarrays
+        if mdarrays:
 	    raidparent = self.tree.append(None)
 	    self.tree[raidparent]['Device'] = _("RAID Devices")
-            for request in raidrequests:
+            for array in mdarrays:
 		mntpt = None
-		if request and request.fstype and request.fstype.getName() == "physical volume (LVM)":
-		    vgreq = self.partitions.getLVMVolumeGroupMemberParent(request)
-		    if vgreq and vgreq.volumeGroupName:
-			if self.show_uneditable:
-			    mntpt = vgreq.volumeGroupName
-			else:
-			    self.tree.appendToHiddenPartitionsList(str(request.uniqueID))
-			    continue
+                if array.format.type == "lvmpv":
+                    vg = None
+		    for _vg in self.storage.vgs:
+                        if _vg.dependsOn(array):
+                            vg = _vg
+                            break
+                    if vg and self.show_uneditable:
+                        mntpt = vg.name
+                    elif vg:
+                        self.tree.appendToHiddenPartitionsList(str(request.uniqueID))
+                        continue
 		    else:
 			mntpt = ""
+                elif array.format.mountable and array.format.mountpoint:
+                    mntpt = array.format.mountpoint
 
                 iter = self.tree.append(raidparent)
 		if mntpt:
                     self.tree[iter]["Mount Point"] = mntpt
+                else:
+                    self.tree[iter]["Mount Point"] = ""
 		    
-                if request and request.mountpoint:
-                    self.tree[iter]["Mount Point"] = request.mountpoint
-		    
-                if request.fstype:
-                    ptype = self.getShortFSTypeName(request.fstype.getName())
-
-                    if request.isEncrypted(self.partitions, True) and request.format:
+                if array.format.type != "none":
+                    ptype = array.format.type
+                    if ptype == "luks" and not array.format.exists:
 			self.tree[iter]['Format'] = self.lock_pixbuf
-                    elif request.format:
+                    elif not array.format.exists:
                         self.tree[iter]['Format'] = self.checkmark_pixbuf
-                    self.tree[iter]['IsFormattable'] = request.fstype.isFormattable()
+                    self.tree[iter]['IsFormattable'] = array.format.formattable
                 else:
                     ptype = _("None")
                     self.tree[iter]['IsFormattable'] = False
 
-		try:
-		    device = "/dev/md%d" % (request.raidminor,)
-		except:
+		if array.minor is not None:
+		    device = "%s" % array.path
+		else:
 		    device = "Auto"
 		    
                 self.tree[iter]['IsLeaf'] = True
                 self.tree[iter]['Device'] = device
-                if request and request.type != REQUEST_NEW and request.fslabel:
-                    self.tree[iter]['Label'] = "%s" % (request.fslabel,)
+                if array.format.exists and getattr(array.format, "label", None):
+                    self.tree[iter]['Label'] = "%s" % array.format.label
                 else:
                     self.tree[iter]['Label'] = ""
                 self.tree[iter]['Type'] = ptype
-                self.tree[iter]['Start'] = ""
-                self.tree[iter]['End'] = ""
-                self.tree[iter]['Size (MB)'] = "%Ld" % (request.getActualSize(self.partitions, self.diskset),)
+                #self.tree[iter]['Start'] = ""
+                #self.tree[iter]['End'] = ""
+                self.tree[iter]['Size (MB)'] = "%Ld" % array.size
                 self.tree[iter]['PyObject'] = str(request.uniqueID)
 
 	# now normal partitions
+        disks = self.storage.disks
 	drvparent = self.tree.append(None)
 	self.tree[drvparent]['Device'] = _("Hard Drives")
-        for drive in drives:
-            disk = self.diskset.disks[drive]
-
+        for disk in disks:
             # add a disk stripe to the graph
-            stripe = self.diskStripeGraph.add(drive, disk)
+            stripe = self.diskStripeGraph.add(disk.name, disk.partedDisk)
 
             # add a parent node to the tree
             parent = self.tree.append(drvparent)
-            self.tree[parent]['Device'] = '/dev/%s' % (drive,)
-            self.tree[parent]['PyObject'] = str('/dev/%s' % (drive,))
-            (cylinders, heads, sectors) = disk.device.biosGeometry
+            self.tree[parent]['Device'] = "%s" % disk.path
+            self.tree[parent]['PyObject'] = "%s" % disk.path
+            (cylinders, heads, sectors) = disk.partedDisk.device.biosGeometry
             sectorsPerCyl = heads * sectors
 
-            extendedParent = None
-            part = disk.getFirstPartition()
+            part = disk.partedDisk.getFirstPartition()
             while part:
                 if part.type & parted.PARTITION_METADATA:
                     part = part.nextPartition()
                     continue
+
+                extendedParent = None
+                partName = part.getDeviceNodeName()
+                device = self.storage.devicetree.getDeviceByName(partName)
+                if not device:
+                    raise RuntimeError("can't find partition %s in device"
+                                       " tree" % partName)
+
                 # ignore the tiny < 1 MB partitions (#119479)
                 if part.getSize(unit="MB") <= 1.0:
-                    if not part.is_active() or not part.getFlag(parted.PARTITION_BOOT):
-                        part = part.nextPartition()
+                    if not part.active or not device.bootable:
                         continue
 
                 stripe.add(part)
-                device = part.getDeviceNodeName()
-                request = self.partitions.getRequestByDeviceName(device)
-
-                if part.type == parted.PARTITION_EXTENDED:
+                if device.isExtended:
                     if extendedParent:
                         raise RuntimeError, ("can't handle more than "
                                              "one extended partition per disk")
                     extendedParent = self.tree.append(parent)
                     iter = extendedParent
-                elif part.type & parted.PARTITION_LOGICAL:
+                elif device.isLogical:
                     if not extendedParent:
                         raise RuntimeError, ("crossed logical partition "
                                              "before extended")
@@ -859,16 +850,20 @@
                     iter = self.tree.append(parent)
                     self.tree[iter]['IsLeaf'] = True
 
-                if request and request.mountpoint:
-                    self.tree[iter]['Mount Point'] = request.mountpoint
+                if device.format.mountable and device.format.mountpoint:
+                    self.tree[iter]['Mount Point'] = device.format.mountpoint
                 else:
                     self.tree[iter]['Mount Point'] = ""
 
-		if request and request.fstype and request.fstype.getName() == "physical volume (LVM)":
-		    vgreq = self.partitions.getLVMVolumeGroupMemberParent(request)
-		    if vgreq and vgreq.volumeGroupName:
+		if device.format.type == "lvmpv":
+		    vg = None
+                    for _vg in self.storage.vgs:
+                        if _vg.dependsOn(part):
+                            vg = _vg
+                            break
+		    if vg and vg.name:
 			if self.show_uneditable:
-			    self.tree[iter]['Mount Point'] = vgreq.volumeGroupName
+			    self.tree[iter]['Mount Point'] = vg.name
 			else:
 			    self.tree.appendToHiddenPartitionsList(part)
 			    self.tree.remove(iter)
@@ -876,26 +871,31 @@
 		    else:
 			self.tree[iter]['Mount Point'] = ""
 
-                    if request.isEncrypted(self.partitions, True) and request.format:
+                    if device.format.type == "luks" and \
+                       not device.format.exists:
                         self.tree[iter]['Format'] = self.lock_pixbuf
-		    elif request.format:
+		    elif not device.format.exists:
 			self.tree[iter]['Format'] = self.checkmark_pixbuf
 		
-                if request and request.fstype:
-                    self.tree[iter]['IsFormattable'] = request.fstype.isFormattable()
+                if device.format.type != "none":
+                    self.tree[iter]['IsFormattable'] = device.format.formattable
 
-                if part.type & parted.PARTITION_FREESPACE:
-                    ptype = _("Free space")
-                elif part.type == parted.PARTITION_EXTENDED:
+                if device.isExtended:
                     ptype = _("Extended")
-                elif part.getFlag(parted.PARTITION_RAID) == 1:
+                elif device.format.type == "mdraid":
                     ptype = _("software RAID")
-		    parreq = self.partitions.getRaidMemberParent(request)
-		    if parreq:
+                    mds = self.storage.mdarrays
+                    array = None
+                    for _array in mds:
+                        if _array.dependsOn(device):
+                            array = _array
+                            break
+		    if array:
 			if self.show_uneditable:
-			    try:
-				mddevice = "/dev/md%d" % (parreq.raidminor,)
-			    except:
+                            if array.minor is not None:
+                                mddevice = "%s" % array.path
+                            else:
+                                mddevice = "Auto"
 				mddevice = "Auto"
 			    self.tree[iter]['Mount Point'] = mddevice
 			else:
@@ -906,41 +906,28 @@
 		    else:
 			self.tree[iter]['Mount Point'] = ""
 
-                    if request and request.isEncrypted(self.partitions, True) and request.format:
-			self.tree[iter]['Format'] = self.lock_pixbuf
-                elif part.fileSystem:
-                    if request and request.fstype != None:
-                        ptype = self.getShortFSTypeName(request.fstype.getName())
-                        if ptype == "foreign":
-                            ptype = map_foreign_to_fsname(part)
-                    else:
-                        ptype = part.fileSystem.type
-
-                    if request and request.isEncrypted(self.partitions, True) and request.format:
+                    if device.format.type == "luks" and \
+                       not device.format.exists:
 			self.tree[iter]['Format'] = self.lock_pixbuf
-                    elif request and request.format:
-			self.tree[iter]['Format'] = self.checkmark_pixbuf
                 else:
-                    if request and request.fstype != None:
-                        ptype = self.getShortFSTypeName(request.fstype.getName())
-
-                        if ptype == "foreign":
-                            ptype = map_foreign_to_fsname(part)
+                    if device.format.type != "none":
+                        ptype = device.format.type
                     else:
                         ptype = _("None")
                 if part.type & parted.PARTITION_FREESPACE:
                     devname = _("Free")
                 else:
-                    devname = '/dev/%s' % (device,)
+                    devname = "%s" % device.path
                 self.tree[iter]['Device'] = devname
-                if request and request.type != REQUEST_NEW and request.fslabel:
-                    self.tree[iter]['Label'] = "%s" % (request.fslabel,)
+                if device.format.exists and
+                   getattr(device.format, "label", None):
+                    self.tree[iter]['Label'] = "%s" % device.format.label
                 else:
                     self.tree[iter]['Label'] = ""
 
                 self.tree[iter]['Type'] = ptype
-                self.tree[iter]['Start'] = str(disk.device.startSectorToCylinder(part.geometry.start))
-                self.tree[iter]['End'] = str(disk.device.endSectorToCylinder(part.geometry.end))
+                #self.tree[iter]['Start'] = str(disk.device.startSectorToCylinder(part.geometry.start))
+                #self.tree[iter]['End'] = str(disk.device.endSectorToCylinder(part.geometry.end))
                 size = part.getSize(unit="MB")
                 if size < 1.0:
                     sizestr = "< 1"
@@ -968,20 +955,22 @@
             self.diskStripeGraph.selectSlice(partition)
 
     def newCB(self, widget):
-        request = NewPartitionSpec(fileSystemTypeGetDefault(), size = 200)
-
-        self.editPartitionRequest(request, isNew = 1)
+        device = self.storage.newPartition()
+        self.editPartition(device, isNew=1)
 
     def deleteCb(self, widget):
         curselection = self.tree.getCurrentPartition()
-
         if curselection:
-            if doDeletePartitionByRequest(self.intf, self.partitions, curselection):
+            if doDeletePartitionByRequest(self.intf,
+                                          self.storage,
+                                          curselection):
                 self.refresh()
         else:
             curdevice = self.tree.getCurrentDevice()
             if curdevice and len(curdevice) > 5:
-                if doDeletePartitionsByDevice(self.intf, self.partitions, self.diskset, curdevice[5:]):
+                if doDeletePartitionsByDevice(self.intf,
+                                              self.storage,
+                                              curdevice[5:]):
                     self.refresh()
                 else:
                     return
@@ -1000,25 +989,10 @@
         self.diskStripeGraph.shutDown()
         self.tree.clear()
 
-	# XXXX - Backup some info which doPartitioning munges if it fails
-	origInfoDict = {}
-	for request in self.partitions.requests:
-	    try:
-		origInfoDict[request.uniqueID] = (request.requestSize, request.currentDrive)
-	    except:
-		pass
-
         try:
-            autopart.doPartitioning(self.diskset, self.partitions)
+            autopart.doPartitioning(self.storage)
             rc = 0
         except PartitioningError, msg:
-	    try:
-		for request in self.partitions.requests:
-		    if request.uniqueID in origInfoDict.keys():
-			(request.requestSize, request.currentDrive) = origInfoDict[request.uniqueID]
-	    except:
-		log.error("Failed to restore original info")
-
             self.intf.messageWindow(_("Error Partitioning"),
                    _("Could not allocate requested partitions: %s.") % (msg),
 				    custom_icon="error")
@@ -1045,10 +1019,11 @@
                 rc = -1
             else:
                 rc = 0
-                reqs = self.partitions.getBootableRequest()
-                if reqs:
-                    for req in reqs:
-                        req.ignoreBootConstraints = 1
+                all_devices = self.storage.devicetree.devices.values()
+                bootDevs = [d for d in all_devices if d.bootable]
+                #if reqs:
+                #    for req in reqs:
+                #        req.ignoreBootConstraints = 1
 
 	if not rc == -1:
 	    self.populate()
@@ -1058,33 +1033,35 @@
     def editCb(self, *args):
         part = self.tree.getCurrentPartition()
 
-        (type, request) = doEditPartitionByRequest(self.intf, self.partitions,
-                                                   part)
-        if request:
+        (type, device) = doEditPartitionByRequest(self.intf,
+                                                  self.storage,
+                                                  part)
+        if device:
             if type == "RAID":
-                self.editRaidRequest(request)
+                self.editRaidArray(device)
 	    elif type == "LVMVG":
-		self.editLVMVolumeGroup(request)
+		self.editLVMVolumeGroup(device)
 	    elif type == "LVMLV":
-		vgrequest = self.partitions.getRequestByID(request.volumeGroup)
-		self.editLVMVolumeGroup(vgrequest)
+		self.editLVMVolumeGroup(device.vg)
             elif type == "NEW":
-		self.editPartitionRequest(request, isNew = 1)
+		self.editPartition(device, isNew = 1)
             else:
-                self.editPartitionRequest(request)
+                self.editPartition(device)
 
     # isNew implies that this request has never been successfully used before
-    def editRaidRequest(self, raidrequest, isNew = 0):
-	raideditor = raid_dialog_gui.RaidEditor(self.partitions,
-						     self.diskset, self.intf,
-						     self.parent, raidrequest,
-						     isNew)
-	origpartitions = self.partitions.copy()
+    def editRaidArray(self, raiddev, isNew = 0):
+	raideditor = raid_dialog_gui.RaidEditor(self.storage,
+						self.intf,
+						self.parent,
+                                                raiddev,
+						isNew)
+        # is a shallow copy enough?
+	origpartitions = self.storage.devicetree.copy()
 	
 	while 1:
-	    request = raideditor.run()
+	    device = raideditor.run()
 
-	    if request is None:
+	    if device is None:
 		return
 
 	    if not isNew:
@@ -1108,29 +1085,29 @@
 	raideditor.destroy()		
 
 
-    def editPartitionRequest(self, origrequest, isNew = 0, restrictfs = None):
+    def editPartition(self, origdevice, isNew = 0, restrictfs = None):
 	parteditor = partition_dialog_gui.PartitionEditor(self.anaconda,
 							  self.parent,
-							  origrequest,
+							  origdevice,
 							  isNew = isNew,
                                                           restrictfs = restrictfs)
 
 	while 1:
-	    request = parteditor.run()
+	    actions = parteditor.run()
 
-	    if request is None:
+	    if not actions:
 		return 0
 
-            if not isNew:
-                self.partitions.removeRequest(origrequest)
+            for action in actions:
+                # XXX we should handle exceptions here
+                self.anaconda.id.storage.devicetree.registerAction(action)
 
-            self.partitions.addRequest(request)
             if self.refresh():
-                # the add failed; remove what we just added and put
-                # back what was there if we removed it
-                self.partitions.removeRequest(request)
-                if not isNew:
-                    self.partitions.addRequest(origrequest)
+                # autopart failed -- cancel the actions and try to get
+                # back to previous state
+                actions.reverse()
+                for action in actions:
+                    self.anaconda.id.storage.devicetree.cancelAction(action)
                 if self.refresh():
                     # this worked before and doesn't now...
                     raise RuntimeError, ("Returning partitions to state "
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/iw/partition_ui_helpers_gui.py anaconda-storage/iw/partition_ui_helpers_gui.py
--- anaconda/iw/partition_ui_helpers_gui.py	2009-02-13 23:03:10.000000000 -0600
+++ anaconda-storage/iw/partition_ui_helpers_gui.py	2009-02-17 13:22:26.000000000 -0600
@@ -28,10 +28,9 @@
 import iutil
 
 from constants import *
-from fsset import *
 from partIntfHelpers import *
-from partRequests import *
 from partedUtils import *
+from storage.formats import device_formats, getFormat
 
 import gettext
 _ = lambda x: gettext.ldgettext("anaconda", x)
@@ -72,13 +71,24 @@
 
     return label
 
+defaultMountPoints = ['/', '/boot', '/home', '/tmp', '/usr',
+                      '/var', '/usr/local', '/opt']
+
+if iutil.isS390():
+    # Many s390 have 2G DASDs, we recomment putting /usr/share on its own DASD
+    defaultMountPoints.insert(5, '/usr/share')
+
+if iutil.isEfi():
+    defaultMountPoints.insert(2, '/boot/efi')
+
 def createMountPointCombo(request, excludeMountPoints=[]):
     mountCombo = gtk.combo_box_entry_new_text()
 
     mntptlist = []
 
-    if request.type != REQUEST_NEW and request.fslabel:
-	mntptlist.append(request.fslabel)
+    label = getattr(request.format, "label", None)
+    if request.exists and isinstance(label, str) and label.startswith("/"):
+	mntptlist.append(label)
         idx = 0
     
     for p in defaultMountPoints:
@@ -90,9 +100,8 @@
 
     map(mountCombo.append_text, mntptlist)
 
-    mountpoint = request.mountpoint
-
-    if request.fstype and request.fstype.isMountable():
+    if request.format.type != "none" and request.format.mountable:
+        mountpoint = request.format.mountpoint
         mountCombo.set_sensitive(1)
         if mountpoint:
             mountCombo.get_children()[0].set_text(mountpoint)
@@ -107,13 +116,14 @@
     return mountCombo
 
 def setMntPtComboStateFromType(fstype, mountCombo):
+    format = getFormat(fstype)
     prevmountable = mountCombo.get_data("prevmountable")
     mountpoint = mountCombo.get_data("saved_mntpt")
 
-    if prevmountable and fstype.isMountable():
+    if prevmountable and format.mountable:
         return
 
-    if fstype.isMountable():
+    if format.mountable:
         mountCombo.set_sensitive(1)
         if mountpoint != None:
             mountCombo.get_children()[0].set_text(mountpoint)
@@ -125,7 +135,7 @@
         mountCombo.get_children()[0].set_text(_("<Not Applicable>"))
         mountCombo.set_sensitive(0)
 
-    mountCombo.set_data("prevmountable", fstype.isMountable())
+    mountCombo.set_data("prevmountable", format.mountable)
 
 def fstypechangeCB(widget, mountCombo):
     fstype = widget.get_active_value()
@@ -134,24 +144,25 @@
 def createAllowedDrivesStore(disks, reqdrives, drivelist, selectDrives=True,
                              disallowDrives=[]):
     drivelist.clear()
-    drives = disks.keys()
-    drives.sort()
-    for drive in drives:
-        size = disks[drive].device.getSize(unit="MB")
+    for disk in disks:
         selected = 0
 
         if selectDrives:
             if reqdrives:
-                if drive in reqdrives:
+                if disk.name in reqdrives:
                     selected = 1
             else:
-                if drive not in disallowDrives:
+                if disk.name not in disallowDrives:
                     selected = 1
 
-        sizestr = "%8.0f MB" % size
-        drivelist.append_row((drive, sizestr, disks[drive].device.model), selected)
+        sizestr = "%8.0f MB" % disk.size
+        # TODO: abstract disk model so we don't have to reach into parted.Disk
+        drivelist.append_row((disk.name,
+                              sizestr,
+                              disk.partedDisk.device.model),
+                             selected)
 
-    if len(disks.keys()) < 2:
+    if len(disks) < 2:
         drivelist.set_sensitive(False)
     else:
         drivelist.set_sensitive(True)
@@ -175,31 +186,33 @@
     store = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_PYOBJECT)
     fstypecombo = datacombo.DataComboBox(store)
     
-    types = fileSystemTypeGetTypes()
+    types = device_formats.keys()
     if availablefstypes:
         names = availablefstypes
     else:
         names = types.keys()
-    if fstype and fstype.isSupported() and fstype.isFormattable():
+    fs = getFormat(fstype)
+    if fs and fs.supported and fs.formattable:
         default = fstype
     else:
-        default = fileSystemTypeGetDefault()
+        default = get_default_filesystem_type()
         
     names.sort()
     defindex = 0
     i = 0
     for name in names:
-        if not fileSystemTypeGet(name).isSupported():
+        format = getFormat(name)
+        if not format.supported:
             continue
 
         if ignorefs and name in ignorefs:
             continue
         
-        if fileSystemTypeGet(name).isFormattable():
-            fstypecombo.append(name, types[name])
-            if default and default.getName() == name:
+        if format.formattable:
+            fstypecombo.append(name, device_formats[name])
+            if default and default.type == name:
                 defindex = i
-                defismountable = types[name].isMountable()
+                defismountable = format.mountable
             i = i + 1
 
     fstypecombo.set_active(defindex)
@@ -209,7 +222,7 @@
 
     if mountCombo:
         mountCombo.set_data("prevmountable",
-                            fstypecombo.get_active_value().isMountable())
+                            fstypecombo.get_active_value()().mountable)
         mountCombo.connect("changed", mountptchangeCB, fstypecombo)
 
     return fstypecombo
@@ -218,7 +231,7 @@
     if iutil.isEfi() and widget.get_children()[0].get_text() == "/boot/efi":
         fstypecombo.set_active_text("efi")
     if widget.get_children()[0].get_text() == "/boot":
-        fstypecombo.set_active_text(fileSystemTypeGetDefaultBoot().getName())
+        fstypecombo.set_active_text(get_default_filesystem_type(boot=True))
 
 def resizeOptionCB(widget, resizesb):
     resizesb.set_sensitive(widget.get_active())
@@ -282,11 +295,11 @@
 def createPreExistFSOptionSection(origrequest, maintable, row, mountCombo,
                                   partitions, ignorefs=[]):
     rc = {}
-    ofstype = origrequest.fstype
+    ofstype = origrequest.format.type
 
     formatcb = gtk.CheckButton(label=_("_Format as:"))
     maintable.attach(formatcb, 0, 1, row, row + 1)
-    formatcb.set_active(istruefalse(origrequest.format))
+    formatcb.set_active(istruefalse(origrequest.format.exists))
     rc["formatcb"] = formatcb
 
     fstypeCombo = createFSTypeMenu(ofstype, fstypechangeCB,
@@ -296,8 +309,9 @@
     row += 1
     rc["fstypeCombo"] = fstypeCombo
 
-    if not formatcb.get_active() and not origrequest.migrate:
-	mountCombo.set_data("prevmountable", ofstype.isMountable())
+    # TODO: sort out fs migration
+    if not formatcb.get_active() and not origrequest.format.migrate:
+	mountCombo.set_data("prevmountable", getFormat(ofstype).mountable)
 
     # this gets added to the table a bit later on
     lukscb = gtk.CheckButton(_("_Encrypt"))
@@ -306,11 +320,12 @@
                     (fstypeCombo, mountCombo, ofstype, lukscb))
 
 
-    if origrequest.origfstype.isMigratable():
+    if origrequest.format.migratable:
 	migratecb = gtk.CheckButton(label=_("Mi_grate filesystem to:"))
-        migratecb.set_active(istruefalse(origrequest.migrate))
+        migratecb.set_active(istruefalse(origrequest.format.migrate))
 
-	migtypes = origrequest.origfstype.getMigratableFSTargets()
+        # TODO: unimplemented
+	migtypes = origrequest.format.migrationTargets
 
 	maintable.attach(migratecb, 0, 1, row, row + 1)
 	migfstypeCombo = createFSTypeMenu(ofstype, None, None,
@@ -326,7 +341,7 @@
 	migratecb = None
 	migfstypeCombo = None
 
-    if origrequest.isResizable(partitions):
+    if origrequest.resizable:
         resizecb = gtk.CheckButton(label=_("_Resize"))
         resizecb.set_active(origrequest.targetSize is not None)
         rc["resizecb"] = resizecb
@@ -357,7 +372,7 @@
 
         formatcb.connect("toggled", formatOptionResizeCB, resizesb)
 
-    if origrequest.encryption:
+    if origrequest.format.type == "luks":
         lukscb.set_active(1)
         lukscb.set_data("encrypted", 1)
     else:
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/kickstart.py anaconda-storage/kickstart.py
--- anaconda/kickstart.py	2009-02-13 23:03:10.000000000 -0600
+++ anaconda-storage/kickstart.py	2009-02-17 13:25:23.000000000 -0600
@@ -24,13 +24,11 @@
 import os
 import tempfile
 from autopart import *
-from fsset import *
 from flags import flags
 from constants import *
 import sys
 import raid
 import string
-import partRequests
 import urlgrabber.grabber as grabber
 import lvm
 import warnings
@@ -149,8 +147,8 @@
         self.handler.id.instClass.setDefaultPartitioning(self.handler.id, doClear = 0)
 
         if self.encrypted:
-            self.handler.id.partitions.autoEncrypt = True
-            self.handler.id.partitions.encryptionPassphrase = self.passphrase
+            self.handler.id.storage.autoEncrypt = True
+            self.handler.id.storage.encryptionPassphrase = self.passphrase
 
         self.handler.skipSteps.extend(["partition", "zfcpconfig", "parttype"])
         return retval
@@ -181,7 +179,7 @@
             self.handler.id.bootloader.doUpgradeOnly = 1
 
         if self.driveorder:
-            hds = isys.hardDriveDict().keys()
+            hds = [d.name for d in self.handler.id.storage.disks]
             for disk in self.driveorder:
                 if disk not in hds:
                     raise KickstartValueError, formatErrorMsg(self.lineno, msg="Specified nonexistent disk %s in driveorder command" % disk)
@@ -269,15 +267,13 @@
     def parse(self, args):
         retval = commands.ignoredisk.F8_IgnoreDisk.parse(self, args)
 
-        diskset = self.handler.id.diskset
         for drive in self.ignoredisk:
-            if not drive in diskset.skippedDisks:
-                diskset.skippedDisks.append(drive)
+            if not drive in self.handler.id.storage.ignoredDisks:
+                self.handler.id.storage.ignoredDisks.append(drive)
 
-        diskset = self.handler.id.diskset
         for drive in self.onlyuse:
-            if not drive in diskset.exclusiveDisks:
-                diskset.exclusiveDisks.append(drive)
+            if not drive in self.handler.id.storage.exclusiveDisks:
+                self.handler.id.storage.exclusiveDisks.append(drive)
 
         return retval
 
@@ -304,8 +300,6 @@
             if self.handler.id.iscsi.addTarget(**kwargs):
                 log.info("added iscsi target: %s" %(target.ipaddr,))
 
-        # FIXME: flush the drive dict so we figure drives out again
-        isys.flushDriveDict()
         return retval
 
 class IscsiName(commands.iscsiname.FC6_IscsiName):
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/livecd.py anaconda-storage/livecd.py
--- anaconda/livecd.py	2009-02-11 21:36:30.000000000 -0600
+++ anaconda-storage/livecd.py	2009-02-15 15:38:05.000000000 -0600
@@ -41,7 +41,6 @@
 import backend
 import isys
 import iutil
-import fsset
 
 import packages
 
@@ -174,16 +173,9 @@
         osimg = self._getLiveBlockDevice() # the real image
         osfd = os.open(osimg, os.O_RDONLY)
 
-        r = anaconda.id.fsset.getEntryByMountPoint("/")
-        rootfs = r.device.setupDevice()
-        rootfd = os.open(rootfs, os.O_WRONLY)
-
-        # set the rootfs to have the right type.  this lets things work
-        # given ext2 or ext3 (and in the future, ext4)
-        # FIXME: should we try to migrate if there isn't a match?
-        roottype = isys.readFSType(osimg)
-        if roottype is not None:
-            r.fsystem = fsset.fileSystemTypeGet(roottype)
+        rootDevice = anaconda.id.fsset.rootDevice
+        rootDevice.setup()
+        rootfd = os.open(rootDevice.path, os.O_WRONLY)
 
         readamt = 1024 * 1024 * 8 # 8 megs at a time
         size = self._getLiveSize()
@@ -222,6 +214,7 @@
         anaconda.id.instProgress = None
 
     def _doFilesystemMangling(self, anaconda):
+        # FIXME: this whole method is a big fucking mess
         log.info("doing post-install fs mangling")
         wait = anaconda.intf.waitWindow(_("Doing post-installation"),
                                         _("Performing post-installation filesystem changes.  This may take several minutes..."))
@@ -233,12 +226,16 @@
         anaconda.id.fsset.mountFilesystems(anaconda)
 
         # restore the label of / to what we think it is
-        r = anaconda.id.fsset.getEntryByMountPoint("/")
-        anaconda.id.fsset.labelEntry(r, anaconda.rootPath, True)
+        rootDevice = anaconda.id.fsset.rootDevice
+        rootDevice.setup()
         # ensure we have a random UUID on the rootfs
         # FIXME: this should be abstracted per filesystem type
-        iutil.execWithRedirect("tune2fs", ["-U", "random", "/dev/%s" % (r.device.getDevice())],
-                               stdout="/dev/tty5", stderr="/dev/tty5",
+        iutil.execWithRedirect("tune2fs",
+                               ["-U",
+                                "random",
+                                rootDevice.path],
+                               stdout="/dev/tty5",
+                               stderr="/dev/tty5",
                                searchPath = 1)
 
         # for any filesystem that's _not_ on the root, we need to handle
@@ -246,6 +243,7 @@
         # this is pretty distasteful, but should work with things like
         # having a separate /usr/local
 
+        # XXX wow, what in the hell is going on here?
         # get a list of fsset entries that are relevant
         entries = sorted(filter(lambda e: not e.fsystem.isKernelFS() and \
                                 e.getMountPoint(), anaconda.id.fsset.entries))
@@ -314,13 +312,12 @@
 
     def _resizeRootfs(self, anaconda, win = None):
         log.info("going to do resize")
-        r = anaconda.id.fsset.getEntryByMountPoint("/")        
-        rootdev = r.device.getDevice()
+        rootDevice = anaconda.id.fsset.rootDevice
 
         # FIXME: we'd like to have progress here to give an idea of
         # how long it will take.  or at least, to give an indefinite
         # progress window.  but, not for this time
-        cmd = ["resize2fs", "/dev/%s" %(rootdev,), "-p"]
+        cmd = ["resize2fs", rootDevice.path, "-p"]
         out = open("/dev/tty5", "w")
         proc = subprocess.Popen(cmd, stdout=out, stderr=out)
         rc = proc.poll()
@@ -376,9 +373,8 @@
         # FIXME: really, this should be in the general sanity checking, but
         # trying to weave that in is a little tricky at present.
         ossize = self._getLiveSizeMB()
-        slash = anaconda.id.partitions.getRequestByMountPoint("/")
-        if slash and \
-           slash.getActualSize(anaconda.id.partitions, anaconda.id.diskset) < ossize:
+        slash = anaconda.id.fsset.rootDevice
+        if slash.size < ossize:
             rc = anaconda.intf.messageWindow(_("Error"),
                                         _("The root filesystem you created is "
                                           "not large enough for this live "
@@ -391,7 +387,6 @@
                 return DISPATCH_BACK
             else:
                 sys.exit(1)
-        
 
     # package/group selection doesn't apply for this backend
     def groupExists(self, group):
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/lvm.py anaconda-storage/lvm.py
--- anaconda/lvm.py	2009-02-13 23:03:10.000000000 -0600
+++ anaconda-storage/lvm.py	1969-12-31 18:00:00.000000000 -0600
@@ -1,614 +0,0 @@
-#
-# lvm.py - lvm probing control
-#
-# Copyright (C) 2002  Red Hat, Inc.  All rights reserved.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-#
-# Author(s): Jeremy Katz <katzj@redhat.com>
-#
-
-import iutil
-import os,sys
-import string
-import math
-import isys
-import re
-
-from flags import flags
-
-import logging
-log = logging.getLogger("anaconda")
-
-from constants import *
-
-MAX_LV_SLOTS=256
-
-lvmDevicePresent = 0
-
-from errors import *
-
-def has_lvm():
-    global lvmDevicePresent
-
-    if not (os.access("/usr/sbin/lvm", os.X_OK) or
-            os.access("/sbin/lvm", os.X_OK)):
-        return
-
-    f = open("/proc/devices", "r")
-    lines = f.readlines()
-    f.close()
-
-    for line in lines:
-        try:
-            (dev, name) = line[:-1].split(' ', 2)
-        except:
-            continue
-        if name == "device-mapper":
-            lvmDevicePresent = 1
-            break
-    return lvmDevicePresent
-# now check to see if lvm is available
-has_lvm()
-        
-def lvmExec(*args):
-    try:
-        return iutil.execWithRedirect("lvm", args, stdout = lvmErrorOutput,
-            stderr = lvmErrorOutput, searchPath = 1)
-    except Exception, e:
-        log.error("error running lvm command: %s" %(e,)) 
-        raise LvmError, args[0]
-
-def lvmCapture(*args):
-    try:
-        lvmout = iutil.execWithCapture("lvm", args, stderr = lvmErrorOutput)
-        lines = []
-        for line in lvmout.split("\n"):
-            lines.append(line.strip().split(':'))
-        return lines
-    except Exception, e:
-        log.error("error running lvm command: %s" %(e,))
-        raise LvmError, args[0]
-
-def vgscan():
-    """Runs vgscan."""
-    global lvmDevicePresent
-        
-    if flags.test or lvmDevicePresent == 0:
-        return
-
-    rc = lvmExec("vgscan", "-v")
-    if rc:
-        log.error("running vgscan failed: %s" %(rc,))
-#        lvmDevicePresent = 0
-
-def vgmknodes(volgroup=None):
-    # now make the device nodes
-    args = ["vgmknodes", "-v"]
-    if volgroup:
-        args.append(volgroup)
-    rc = lvmExec(*args)
-    if rc:
-        log.error("running vgmknodes failed: %s" %(rc,))
-#        lvmDevicePresent = 0
-
-def vgcheckactive(volgroup = None):
-    """Check if volume groups are active
-
-    volgroup - optional parameter to inquire about a specific volume group.
-    """
-    global lvmDevicePresent
-    if flags.test or lvmDevicePresent == 0:
-        return False
-
-    args = ["lvs", "--noheadings", "--units", "b", "--nosuffix",
-            "--separator", ":", "--options", "vg_name,lv_name,attr"]
-    for line in lvmCapture(*args):
-        try:
-            (vg, lv, attr) = line
-        except:
-            continue
-
-        log.info("lv %s/%s, attr is %s" %(vg, lv, attr))
-        if attr.find("a") == -1:
-            continue
-
-        if volgroup is None or volgroup == vg:
-            return True
-
-    return False
-
-def vgactivate(volgroup = None):
-    """Activate volume groups by running vgchange -ay.
-
-    volgroup - optional single volume group to activate
-    """
-    global lvmDevicePresent
-    if flags.test or lvmDevicePresent == 0:
-        return
-
-    args = ["vgchange", "-ay", "-v"]
-    if volgroup:
-        args.append(volgroup)
-    rc = lvmExec(*args)
-    if rc:
-        log.error("running vgchange failed: %s" %(rc,))
-#        lvmDevicePresent = 0
-    vgmknodes(volgroup)
-
-def vgdeactivate(volgroup = None):
-    """Deactivate volume groups by running vgchange -an.
-
-    volgroup - optional single volume group to deactivate
-    """
-    global lvmDevicePresent
-    if flags.test or lvmDevicePresent == 0:
-        return
-
-    args = ["vgchange", "-an", "-v"]
-    if volgroup:
-        args.append(volgroup)
-    rc = lvmExec(*args)
-    if rc:
-        log.error("running vgchange failed: %s" %(rc,))
-#        lvmDevicePresent = 0
-
-def lvcreate(lvname, vgname, size):
-    """Creates a new logical volume.
-
-    lvname - name of logical volume to create.
-    vgname - name of volume group lv will be in.
-    size - size of lv, in megabytes.
-    """
-    global lvmDevicePresent
-    if flags.test or lvmDevicePresent == 0:
-        return
-    writeForceConf()
-    vgscan()
-
-    args = ["lvcreate", "-v", "-L", "%dM" %(size,), "-n", lvname, "-An", vgname]
-    try:
-        rc = lvmExec(*args)
-    except:
-        rc = 1
-    if rc:
-        raise LVCreateError(vgname, lvname, size)
-    unlinkConf()
-
-def lvremove(lvname, vgname):
-    """Removes a logical volume.
-
-    lvname - name of logical volume to remove.
-    vgname - name of volume group lv is in.
-    """
-    global lvmDevicePresent
-    if flags.test or lvmDevicePresent == 0:
-        return
-
-    args = ["lvremove", "-f", "-v"]
-    dev = "/dev/%s/%s" %(vgname, lvname)
-    args.append(dev)
-
-    try:
-        rc = lvmExec(*args)
-    except:
-        rc = 1
-    if rc:
-        raise LVRemoveError(vgname, lvname)
-
-def lvresize(lvname, vgname, size):
-    global lvmDevicePresent
-    if flags.test or lvmDevicePresent == 0:
-        return
-
-    args = ["lvresize", "-An", "-L", "%dM" %(size,), "-v", "--force",
-            "/dev/%s/%s" %(vgname, lvname,)]
-
-    try:
-        rc = lvmExec(*args)
-    except:
-        rc = 1
-    if rc:
-        raise LVResizeError(vgname, lvname)
-
-
-def vgcreate(vgname, PESize, nodes):
-    """Creates a new volume group."
-
-    vgname - name of volume group to create.
-    PESize - Physical Extent size, in kilobytes.
-    nodes - LVM Physical Volumes on which to put the new VG.
-    """
-    global lvmDevicePresent
-    if flags.test or lvmDevicePresent == 0:
-        return
-
-    # rescan now that we've recreated pvs.  ugh.
-    writeForceConf()
-    vgscan()
-
-    args = ["vgcreate", "-v", "-An", "-s", "%sk" % (PESize,), vgname ]
-    args.extend(nodes)
-
-    try:
-        rc = lvmExec(*args)
-    except:
-        rc = 1
-    if rc:
-        raise VGCreateError(vgname, PESize, nodes)
-    unlinkConf()
-
-def vgremove(vgname):
-    """Removes a volume group.  Deactivates the volume group first
-
-    vgname - name of volume group.
-    """
-    global lvmDevicePresent
-    if flags.test or lvmDevicePresent == 0:
-        return
-
-    # find the Physical Volumes which make up this Volume Group, so we
-    # can prune and recreate them.
-    pvs = []
-    for pv in pvlist():
-        if pv[1] == vgname:
-            pvs.append(pv[0])
-
-    # we'll try to deactivate... if it fails, we'll probably fail on
-    # the removal too... but it's worth a shot
-    try:
-        vgdeactivate(vgname)
-    except:
-        pass
-
-    args = ["vgremove", "-v", vgname]
-
-    log.info(string.join(args, ' '))
-    try:
-        rc = lvmExec(*args)
-    except:
-        rc = 1
-    if rc:
-        raise VGRemoveError, vgname
-
-    # now iterate all the PVs we've just freed up, so we reclaim the metadata
-    # space.  This is an LVM bug, AFAICS.
-    for pvname in pvs:
-        args = ["pvremove", "-ff", "-y", "-v", pvname]
-
-        log.info(string.join(args, ' '))
-        try:
-            rc = lvmExec(*args)
-        except:
-            rc = 1
-        if rc:
-            raise PVRemoveError, pvname
-
-        args = ["pvcreate", "-ff", "-y", "-v", pvname]
-
-        log.info(string.join(args, ' '))
-        try:
-            rc = lvmExec(*args)
-        except:
-            rc = 1
-        if rc:
-            raise PVCreateError, pvname
-        wipeOtherMetadataFromPV(pvname)
-
-def pvcreate(node):
-    """Initializes a new Physical Volume."
-
-    node - path to device node on which to create the new PV."
-    """
-    global lvmDevicePresent
-    if flags.test or lvmDevicePresent == 0:
-        return
-
-    # rescan now that we've recreated pvs.  ugh.
-    writeForceConf()
-
-    args = ["pvcreate", "-ff", "-y", "-v", node ]
-
-    try:
-        rc = lvmExec(*args)
-    except:
-        rc = 1
-    if rc:
-        raise PVCreateError, node
-    unlinkConf()
-    wipeOtherMetadataFromPV(node)
-
-def lvlist():
-    global lvmDevicePresent
-    if lvmDevicePresent == 0:
-        return []
-
-    lvs = []
-    # field names for "options" are in LVM2.2.01.01/lib/report/columns.h
-    args = ["lvdisplay", "-C", "--noheadings", "--units", "b",
-            "--nosuffix", "--separator", ":", "--options",
-            "vg_name,lv_name,lv_size,origin"
-           ]
-    lvscanout = iutil.execWithCapture("lvm", args, stderr = "/dev/tty6")
-    for line in lvmCapture(*args):
-        try:
-            (vg, lv, size, origin) = line
-            size = long(math.floor(long(size) / (1024 * 1024)))
-            if origin == '':
-                origin = None
-        except:
-            continue
-
-        logmsg = "lv is %s/%s, size of %s" % (vg, lv, size)
-        if origin:
-            logmsg += ", snapshot from %s" % (origin,)
-        log.info(logmsg)
-        lvs.append( (vg, lv, size, origin) )
-
-    return lvs
-
-def pvlist():
-    global lvmDevicePresent
-    if lvmDevicePresent == 0:
-        return []
-
-    pvs = []
-    args = ["pvdisplay", "-C", "--noheadings", "--units", "b",
-            "--nosuffix", "--separator", ":", "--options",
-            "pv_name,vg_name,dev_size"
-           ]
-    for line in lvmCapture(*args):
-        try:
-            (dev, vg, size) = line
-            size = long(math.floor(long(size) / (1024 * 1024)))
-        except:
-            continue
-
-        if dev.startswith("/dev/dm-"):
-            from block import dm
-            try:
-                sb = os.stat(dev)
-                (major, minor) = (os.major(sb.st_rdev), os.minor(sb.st_rdev))
-                for map in dm.maps():
-                    if map.dev.major == major and map.dev.minor == minor:
-                        dev = "/dev/mapper/%s" % map.name
-                        break
-            except:
-                pass
-
-        log.info("pv is %s in vg %s, size is %s" %(dev, vg, size))
-        pvs.append( (dev, vg, size) )
-
-    return pvs
-    
-def vglist():
-    global lvmDevicePresent
-    if lvmDevicePresent == 0:
-        return []
-
-    vgs = []
-    args = ["vgdisplay", "-C", "--noheadings", "--units", "b",
-            "--nosuffix", "--separator", ":", "--options",
-            "vg_name,vg_size,vg_extent_size,vg_free"
-           ]
-    for line in lvmCapture(*args):
-        try:
-            (vg, size, pesize, free) = line
-            size = long(math.floor(long(size) / (1024 * 1024)))
-            pesize = long(pesize)/1024
-            free = math.floor(long(free) / (1024 * 1024))
-        except:
-            continue
-        log.info("vg %s, size is %s, pesize is %s" %(vg, size, pesize))
-        vgs.append( (vg, size, pesize, free) )
-    return vgs
-
-def partialvgs():
-    global lvmDevicePresent
-    if lvmDevicePresent == 0:
-        return []
-    
-    vgs = []
-    args = ["vgdisplay", "-C", "-P", "--noheadings", "--units", "b",
-            "--nosuffix", "--separator", ":"]
-    for line in lvmCapture(*args):
-        try:
-            (vg, numpv, numlv, numsn, attr, size, free) = line
-        except:
-            continue
-        if attr.find("p") != -1:
-            log.info("vg %s, attr is %s" %(vg, attr))
-            vgs.append(vg)
-
-    return vgs
-
-# FIXME: this is a hack.  we really need to have a --force option.
-def unlinkConf():
-    lvmroot = "/etc/lvm"
-    if os.path.exists("%s/lvm.conf" %(lvmroot,)):
-        os.unlink("%s/lvm.conf" %(lvmroot,))
-
-def writeForceConf():
-    """Write out an /etc/lvm/lvm.conf that doesn't do much (any?) filtering"""
-
-    lvmroot = "/etc/lvm"
-    try:
-        os.unlink("/etc/lvm/.cache")
-    except:
-        pass
-    if not os.path.isdir(lvmroot):
-        os.mkdir(lvmroot)
-
-    unlinkConf()
-
-    f = open("%s/lvm.conf" %(lvmroot,), "w+")
-    f.write("""
-# anaconda hacked lvm.conf to avoid filtering breaking things
-devices {
-  sysfs_scan = 0
-  md_component_detection = 1
-}
-""")
-
-# FIXME: another hack.  we need to wipe the raid metadata since pvcreate
-# doesn't
-def wipeOtherMetadataFromPV(node):
-    try:
-        isys.wipeRaidSB(node)
-    except Exception, e:
-        log.critical("error wiping raidsb from %s: %s", node, e)
-        
-    
-
-def getPossiblePhysicalExtents(floor=0):
-    """Returns a list of integers representing the possible values for
-       the physical extent of a volume group.  Value is in KB.
-
-       floor - size (in KB) of smallest PE we care about.
-    """
-
-    possiblePE = []
-    curpe = 8
-    while curpe <= 16384*1024:
-	if curpe >= floor:
-	    possiblePE.append(curpe)
-	curpe = curpe * 2
-
-    return possiblePE
-
-def clampLVSizeRequest(size, pe, roundup=0):
-    """Given a size and a PE, returns the actual size of logical volumne.
-
-    size - size (in MB) of logical volume request
-    pe   - PE size (in KB)
-    roundup - round sizes up or not
-    """
-
-    if roundup:
-        func = math.ceil
-    else:
-        func = math.floor
-    return (long(func((size*1024L)/pe))*pe)/1024
-
-def clampPVSize(pvsize, pesize):
-    """Given a PV size and a PE, returns the usable space of the PV.
-    Takes into account both overhead of the physical volume and 'clamping'
-    to the PE size.
-
-    pvsize - size (in MB) of PV request
-    pesize - PE size (in KB)
-    """
-
-    # we want Kbytes as a float for our math
-    pvsize *= 1024.0
-    return long((math.floor(pvsize / pesize) * pesize) / 1024)
-
-def getMaxLVSize(pe):
-    """Given a PE size in KB, returns maximum size (in MB) of a logical volume.
-
-    pe - PE size in KB
-    """
-
-    if os.uname()[2][:4]=="2.4.":
-        return pe*64 #2.4 kernel - LVM1, max size is 2TiB and depends on extent size/count
-
-    else: #newer kernel - LVM2, size limited by number of sectors
-        if productArch in ("x86_64", "ppc64"): #64bit architectures
-            return (8*1024*1024*1024*1024) #Max is 8EiB (very large number..)
-        else:
-            return (16*1024*1024) #Max is 16TiB
-
-def safeLvmName(str):
-    tmp = string.strip(str)
-    tmp = tmp.replace("/", "_")
-    tmp = re.sub("[^0-9a-zA-Z._]", "", str)
-    tmp = tmp.lstrip("_")
-
-    return tmp
-
-def createSuggestedVGName(partitions, network):
-    """Given list of partition requests, come up with a reasonable VG name
-
-    partitions - list of requests
-    """
-
-    # try to create a volume group name incorporating the hostname
-    hn = network.hostname
-    if hn is not None and hn != '':
-        if hn == 'localhost' or hn == 'localhost.localdomain':
-            vgtemplate = "VolGroup"
-        elif hn.find('.') != -1:
-            hn = safeLvmName(hn)
-            vgtemplate = "vg_%s" % (hn.split('.')[0].lower(),)
-        else:
-            hn = safeLvmName(hn)
-            vgtemplate = "vg_%s" % (hn.lower(),)
-    else:
-        vgtemplate = "VolGroup"
-
-    if not partitions.isVolumeGroupNameInUse(vgtemplate):
-        return vgtemplate
-    else:
-        i = 0
-        while 1:
-            tmpname = "%s%02d" % (vgtemplate, i,)
-            if not partitions.isVolumeGroupNameInUse(tmpname):
-                break
-
-            i += 1
-            if i > 99:
-                tmpname = ""
-
-        return tmpname
-
-def createSuggestedLVName(logreqs):
-    """Given list of LV requests, come up with a reasonable LV name
-
-    partitions - list of LV requests for this VG
-    """
-
-    i = 0
-
-    lnames = []
-    for lv in logreqs:
-        lnames.append(lv.logicalVolumeName)
-
-    while 1:
-        tmpname = "LogVol%02d" % (i,)
-        if (logreqs is None) or (tmpname not in lnames):
-            break
-
-        i += 1
-        if i > 99:
-            tmpname = ""
-
-    return tmpname
-
-def getVGUsedSpace(vgreq, requests, diskset):
-    vgused = 0
-    for request in requests.requests:
-	if request.type == REQUEST_LV and request.volumeGroup == vgreq.uniqueID:
-	    size = int(request.getActualSize(requests, diskset))
-	    vgused = vgused + size
-
-
-    return vgused
-
-def getVGFreeSpace(vgreq, requests, diskset):
-    used = getVGUsedSpace(vgreq, requests, diskset)
-    log.debug("used space is %s" % (used,))
-    
-    total = vgreq.getActualSize(requests, diskset)
-    log.debug("actual space is %s" % (total,))
-    return total - used
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/network.py anaconda-storage/network.py
--- anaconda/network.py	2009-02-11 21:36:30.000000000 -0600
+++ anaconda-storage/network.py	2009-02-17 13:26:12.000000000 -0600
@@ -585,8 +585,8 @@
             # tell NM not to touch the interface(s) actually used for /, but we
             # have no logic to determine that
             if anaconda is not None:
-                rootdev = anaconda.id.fsset.getEntryByMountPoint("/").device
-                if rootdev.isNetdev():
+                rootdev = anaconda.id.storage.fsset.rootDevice
+                if rootdev.isNetdev:
                     f.write("NM_CONTROLLED=no\n")
 
             f.close()
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/packages.py anaconda-storage/packages.py
--- anaconda/packages.py	2009-02-13 23:03:10.000000000 -0600
+++ anaconda-storage/packages.py	2009-02-17 12:52:20.000000000 -0600
@@ -174,12 +174,14 @@
                                         type="custom", custom_icon="error", custom_buttons=[_("_Reboot")])
 	            sys.exit(0)
 
-            anaconda.id.fsset.formatSwap(anaconda.rootPath)
-            anaconda.id.fsset.turnOnSwap(anaconda.rootPath)
-            anaconda.id.fsset.makeFilesystems(anaconda.rootPath,
-                                              anaconda.backend.skipFormatRoot)
-            anaconda.id.fsset.mountFilesystems(anaconda,0,0,
-                                               anaconda.backend.skipFormatRoot)
+            anaconda.id.storage.processActions(anaconda.intf)
+            anaconda.id.fsset.turnOnSwap(anaconda.intf)
+            #anaconda.id.fsset.makeFilesystems(anaconda.rootPath,
+            #                                  anaconda.backend.skipFormatRoot)
+            anaconda.id.storage.fsset.mountFilesystems(anaconda,
+                                                       raiseErrors=False,
+                                                       readOnly=False,
+                                                       anaconda.backend.skipFormatRoot)
 
 def setupTimezone(anaconda):
     # we don't need this on an upgrade or going backwards
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/partedUtils.py anaconda-storage/partedUtils.py
--- anaconda/partedUtils.py	2009-02-13 23:03:10.000000000 -0600
+++ anaconda-storage/partedUtils.py	2009-02-17 12:13:27.000000000 -0600
@@ -49,72 +49,6 @@
 import gettext
 _ = lambda x: gettext.ldgettext("anaconda", x)
 
-def get_partition_file_system_type(part):
-    """Return the file system type of the parted.Partition part.
-
-    Arguments:
-    part -- parted.Partition object
-
-    Return:
-    Filesystem object (as defined in fsset.py)
-    """
-    if part.fileSystem is None and part.getFlag(parted.PARTITION_PREP):
-        ptype = fsset.fileSystemTypeGet("PPC PReP Boot")
-    elif part.fileSystem is None:
-        return None
-    elif (part.getFlag(parted.PARTITION_BOOT) and
-          part.getSize(unit="MB") <= 1 and part.fileSystem.type == "hfs"):
-        ptype = fsset.fileSystemTypeGet("Apple Bootstrap")
-    elif part.fileSystem.type == "linux-swap":
-        ptype = fsset.fileSystemTypeGet("swap")
-    elif isEfiSystemPartition(part):
-        ptype = fsset.fileSystemTypeGet("efi")
-    elif isEfiSystemPartition(part):
-        ptype = fsset.fileSystemTypeGet("efi")
-    elif part.fileSystem.type in ("fat16", "fat32"):
-        ptype = fsset.fileSystemTypeGet("vfat")
-    else:
-        try:
-            ptype = fsset.fileSystemTypeGet(part.fileSystem.type)
-        except:
-            ptype = fsset.fileSystemTypeGet("foreign")
-
-    return ptype
-
-
-def set_partition_file_system_type(part, fstype):
-    """Set partition type of part to PedFileSystemType implied by fstype."""
-    if fstype == None:
-        return
-    try:
-        for flag in fstype.getPartedPartitionFlags():
-            if not part.isFlagAvailable(flag):
-                raise PartitioningError, ("requested file system type needs "
-                                          "a flag that is not available.")
-            part.setFlag(flag)
-        if isEfiSystemPartition(part):
-            part.system = parted.fileSystemType["fat32"]
-        else:
-            part.system = fstype.getPartedFileSystemType()
-    except:
-        print("Failed to set partition type to ", fstype.getName())
-        pass
-
-def get_partition_drive(partition):
-    """Return the device name for disk that PedPartition partition is on."""
-    return partition.geometry.device.path[5:]
-
-def map_foreign_to_fsname(part):
-    """Return the partition type associated with the numeric type.""" 
-    return part._fileSystem._type.name
-
-def filter_partitions(disk, func):
-    rc = []
-    for part in disk.partitions:
-        if func(part):
-            rc.append(part)
-    return rc
-
 def getDefaultDiskType():
     """Get the default partition table type for this architecture."""
     if iutil.isEfi():
@@ -244,64 +178,6 @@
     else:
         return 1
 
-def hasProtectedPartitions(drive, anaconda):
-    rc = False
-    if anaconda is None:
-        return rc
-
-    try:
-        for protected in anaconda.id.partitions.protectedPartitions():
-            if protected.startswith(drive):
-                part = protected[len(drive):]
-                if part[0] == "p":
-                    part = part[1:]
-                if part.isdigit():
-                    rc = True
-                    break
-    except:
-        pass
-
-    return rc
-
-# attempt to associate a parted filesystem type on a partition that
-# didn't probe as one type or another.
-def validateFsType(part):
-    # we only care about primary and logical partitions
-    if not part.type in (parted.PARTITION_NORMAL,
-                         parted.PARTITION_LOGICAL):
-        return
-
-    if part.getFlag(parted.PARTITION_LVM) or part.getFlag(parted.PARTITION_RAID):
-        return
-
-    # if the partition already has a type, no need to search
-    if part.fileSystem:
-        return
-
-    # first fsystem to probe wins, so sort the types into a preferred
-    # order.
-    fsnames = parted.fileSystemType.keys()
-    goodTypes = ['ext3', 'ext2']
-    badTypes = ['linux-swap',]
-    for fstype in goodTypes:
-        fsnames.remove(fstype)
-    fsnames = goodTypes + fsnames
-    for fstype in badTypes:
-        fsnames.remove(fstype)
-    fsnames.extend(badTypes)
-
-    # now check each type, and set the partition system accordingly.
-    for fsname in fsnames:
-        fstype = parted.fileSystemType[fsname]
-        try:
-            parted.probeForSpecificFileSystem(fstype.name, part.geometry)
-            # XXX verify that this will not modify system type
-            # in the case where a user does not modify partitions
-            part.system = fstype
-            return
-        except (parted.FileSystemException, parted.IOException):
-            pass
-
 def isLinuxNative(part):
     """Check if the type is a 'Linux native' filesystem."""
     if part.getFlag(parted.PARTITION_RAID) or part.getFlag(parted.PARTITION_LVM) or \
@@ -401,13 +277,6 @@
         self.anaconda = anaconda
         self.devicesOpen = False
 
-    def onlyPrimaryParts(self):
-        for disk in self.disks.values():
-            if disk.supportsFeature(parted.DISK_TYPE_EXTENDED):
-                return 0
-
-        return 1
-
     def startMPath(self):
         """Start all of the dm multipath devices associated with the DiskSet."""
 
@@ -470,289 +339,6 @@
             dmraid.stopAllRaid(DiskSet.dmList)
             DiskSet.dmList = None
 
-    def startMdRaid(self):
-        """Start all of the md raid devices associated with the DiskSet."""
-
-        testList = []
-        testList.extend(DiskSet.skippedDisks)
-
-        for mp in DiskSet.mpList or []:
-            for m in mp.members:
-                disk = m.split('/')[-1]
-                testList.append(disk)
-
-        if not iutil.isS390():
-            for rs in DiskSet.dmList or []:
-                for m in rs.members:
-                    if isinstance(m, block.RaidDev):
-                        disk = m.rd.device.path.split('/')[-1]
-                        testList.append(disk)
-
-        driveList = filter(lambda x: x not in testList, self.driveList())
-        DiskSet.mdList.extend(raid.startAllRaid(driveList))
-
-    def stopMdRaid(self):
-        """Stop all of the md raid devices associated with the DiskSet."""
-
-        raid.stopAllRaid(DiskSet.mdList)
-
-        while DiskSet.mdList:
-            DiskSet.mdList.pop()
-
-    def getInfo(self, readFn=lambda d: isys.readFSLabel(d)):
-        """Return a dict keyed on device name, storing some sort of data
-           about each device.  This is typially going to be labels or UUIDs,
-           as required by readFstab.
-        """
-        ret = {}
-
-        encryptedDevices = self.anaconda.id.partitions.encryptedDevices
-
-        for drive in self.driveList():
-            # Don't read labels from drives we cleared using clearpart, as
-            # we don't actually remove the existing filesystems so those
-            # labels will still be present (#209291).
-            if drive in DiskSet.skippedDisks:
-                continue
-
-            # ignoredisk takes precedence over clearpart (#186438).
-            if DiskSet.exclusiveDisks != [] and \
-                    drive not in DiskSet.exclusiveDisks:
-                continue
-
-            disk = self.disks[drive]
-            func = lambda part: (part.active and
-                                 not (part.getFlag(parted.PARTITION_RAID)
-                                      or part.getFlag(parted.PARTITION_LVM)))
-            parts = filter_partitions(disk, func)
-            for part in parts:
-                node = part.getDeviceNodeName()
-                crypto = encryptedDevices.get(node)
-                if crypto and not crypto.openDevice():
-                    node = crypto.getDevice()
-
-                val = readFn(node)
-                if val:
-                    ret[node] = val
-
-                if crypto:
-                    crypto.closeDevice()
-
-        # not doing this right now, because we should _always_ have a
-        # partition table of some kind on dmraid.
-        #if False:
-        #    for rs in DiskSet.dmList or [] + DiskSet.mpList or []:
-        #        label = isys.readFSLabel(rs.name)
-        #        if label:
-        #            labels[rs.name] = label
-
-        for dev, devices, level, numActive in DiskSet.mdList:
-            crypto = encryptedDevices.get(dev)
-            if crypto and not crypto.openDevice():
-                dev = crypto.getDevice()
-
-            val = readFn(dev)
-            if val:
-                ret[dev] = val
-
-            if crypto:
-                crypto.closeDevice()
-
-        active = lvm.vgcheckactive()
-        if not active:
-            lvm.vgscan()
-            lvm.vgactivate()
-
-        for (vg, lv, size, lvorigin) in lvm.lvlist():
-            if lvorigin:
-                continue
-            node = "%s/%s" % (vg, lv)
-            crypto = encryptedDevices.get(node)
-            if crypto and not crypto.openDevice():
-                node = crypto.getDevice()
-
-            val = readFn("/dev/" + node)
-            if val:
-                ret[node] = val
-
-            if crypto:
-                crypto.closeDevice()
-
-        if not active:
-            lvm.vgdeactivate()
-
-        return ret
-
-    def findExistingRootPartitions(self, upgradeany = 0):
-        """Return a list of all of the partitions which look like a root fs."""
-        rootparts = []
-
-        self.startMPath()
-        self.startDmRaid()
-        self.startMdRaid()
-
-        for dev, crypto in self.anaconda.id.partitions.encryptedDevices.items():
-            # FIXME: order these so LVM and RAID always work on the first try
-            if crypto.openDevice():
-                log.error("failed to open encrypted device %s" % (dev,))
-
-        if flags.cmdline.has_key("upgradeany"):
-            upgradeany = 1
-
-        for dev, devices, level, numActive in self.mdList:
-            (errno, msg) = (None, None)
-            found = 0
-            theDev = dev
-            crypto = self.anaconda.id.partitions.encryptedDevices.get(dev)
-            if crypto and not crypto.openDevice():
-                theDev = "/dev/%s" % (crypto.getDevice(),)
-            elif crypto:
-                log.error("failed to open encrypted device %s" % dev)
-
-            fs = isys.readFSType(theDev)
-            if fs is not None:
-                try:
-                    isys.mount(theDev, self.anaconda.rootPath, fs, readOnly = 1)
-                    found = 1
-                except SystemError:
-                    pass
-
-            if found:
-                if os.access (self.anaconda.rootPath + '/etc/fstab', os.R_OK):
-                    relstr = getReleaseString(self.anaconda.rootPath)
-
-                    if ((upgradeany == 1) or
-                        (productMatches(relstr, productName))):
-                        try:
-                            label = isys.readFSLabel(theDev)
-                        except:
-                            label = None
-
-                        uuid = isys.readFSUuid(theDev)
-                        # XXX we could add the "raw" dev and let caller decrypt
-                        rootparts.append ((theDev, fs, relstr, label, uuid))
-                isys.umount(self.anaconda.rootPath)
-
-        # now, look for candidate lvm roots
-        lvm.vgscan()
-        lvm.vgactivate()
-
-        for dev, crypto in self.anaconda.id.partitions.encryptedDevices.items():
-            # FIXME: order these so LVM and RAID always work on the first try
-            if crypto.openDevice():
-                log.error("failed to open encrypted device %s" % (dev,))
-
-        for (vg, lv, size, lvorigin) in lvm.lvlist():
-            if lvorigin:
-                continue
-            dev = "/dev/%s/%s" %(vg, lv)
-            found = 0
-            theDev = dev
-            node = "%s/%s" % (vg, lv)
-            dmnode = "mapper/%s-%s" % (vg, lv)
-            crypto = self.anaconda.id.partitions.encryptedDevices.get(dmnode)
-            if crypto and not crypto.openDevice():
-                theDev = "/dev/%s" % (crypto.getDevice(),)
-            elif crypto:
-                log.error("failed to open encrypted device %s" % dev)
-
-            fs = isys.readFSType(theDev)
-            if fs is not None:
-                try:
-                    isys.mount(theDev, self.anaconda.rootPath, fs, readOnly = 1)
-                    found = 1
-                except SystemError:
-                    pass
-
-            if found:
-                if os.access (self.anaconda.rootPath + '/etc/fstab', os.R_OK):
-                    relstr = getReleaseString(self.anaconda.rootPath)
-
-                    if ((upgradeany == 1) or
-                        (productMatches(relstr, productName))):
-                        try:
-                            label = isys.readFSLabel(theDev)
-                        except:
-                            label = None
-
-                        uuid = isys.readFSUuid(theDev)
-                        rootparts.append ((theDev, fs, relstr, label, uuid))
-                isys.umount(self.anaconda.rootPath)
-
-        lvm.vgdeactivate()
-
-        # don't stop raid until after we've looked for lvm on top of it
-        self.stopMdRaid()
-
-        drives = self.disks.keys()
-        drives.sort()
-
-        protected = self.anaconda.id.partitions.protectedPartitions()
-
-        for drive in drives:
-            disk = self.disks[drive]
-            for part in disk.partitions:
-                node = part.getDeviceNodeName()
-                crypto = self.anaconda.id.partitions.encryptedDevices.get(node)
-                if (part.active
-                    and (part.getFlag(parted.PARTITION_RAID)
-                         or part.getFlag(parted.PARTITION_LVM))):
-                    continue
-                elif part.fileSystem or crypto:
-                    theDev = node
-                    if part.fileSystem:
-                        fstype = part.fileSystem.type
-                    else:
-                        fstype = None
-
-                    # parted doesn't tell ext4 from ext3
-                    if fstype == "ext3": 
-                        fstype = isys.readFSType(theDev)
-
-                    if crypto and not crypto.openDevice():
-                        theDev = crypto.getDevice()
-                        fstype = isys.readFSType("/dev/%s" % theDev)
-                    elif crypto:
-                        log.error("failed to open encrypted device %s" % node)
-
-                    if not fstype or fstype not in fsset.getUsableLinuxFs():
-                        continue
-
-                    try:
-                        isys.mount("/dev/%s" % (theDev,),
-                                   self.anaconda.rootPath, fstype)
-                        checkRoot = self.anaconda.rootPath
-                    except SystemError:
-                        continue
-
-                    if os.access (checkRoot + '/etc/fstab', os.R_OK):
-                        relstr = getReleaseString(checkRoot)
-
-                        if ((upgradeany == 1) or
-                            (productMatches(relstr, productName))):
-                            try:
-                                label = isys.readFSLabel("/dev/%s" % theDev)
-                            except:
-                                label = None
-
-                            uuid = isys.readFSUuid("/dev/%s" % (theDev,))
-                            rootparts.append (("/dev/%s" % (theDev,),
-                                              fstype, relstr, label, uuid))
-
-                    isys.umount(self.anaconda.rootPath)
-
-        return rootparts
-
-    def driveList (self):
-        """Return the list of drives on the system."""
-        drives = isys.hardDriveDict().keys()
-        drives.sort (isys.compareDrives)
-        return drives
-
-    def drivesByName (self):
-        """Return a dictionary of the drives on the system."""
-        return isys.hardDriveDict()
-
     def savePartitions (self):
         """Write the partition tables out to the disks."""
         for disk in self.disks.values():
@@ -781,39 +367,6 @@
             del disk
         self.refreshDevices()
 
-    def _addDisk(self, drive, disk):
-        log.debug("adding drive %s to disk list" % (drive,))
-        self.initializedDisks[drive] = True
-        self.disks[drive] = disk
-
-    def _removeDisk(self, drive, addSkip=True):
-        msg = "removing drive %s from disk lists" % (drive,)
-        if addSkip:
-            msg += "; adding to skip list"
-        log.debug(msg)
-
-        if self.disks.has_key(drive):
-            del self.disks[drive]
-        if addSkip:
-            if self.initializedDisks.has_key(drive):
-                del self.initializedDisks[drive]
-            DiskSet.skippedDisks.append(drive)
-
-    def refreshDevices (self):
-        """Reread the state of the disks as they are on disk."""
-        self.closeDevices()
-        self.disks = {}
-        self.openDevices()
-
-    def closeDevices (self):
-        """Close all of the disks which are open."""
-        self.stopDmRaid()
-        self.stopMPath()
-        for disk in self.disks.keys():
-            #self.disks[disk].close()
-            del self.disks[disk]
-        self.devicesOpen = False
-
     def isDisciplineFBA (self, drive):
         if not iutil.isS390():
             return False
@@ -1107,57 +660,6 @@
                     pass
         self.devicesOpen = True
 
-    def partitionTypes (self):
-        """Return list of (partition, partition type) tuples for all parts."""
-        rc = []
-        drives = self.disks.keys()
-        drives.sort()
-
-        for drive in drives:
-            disk = self.disks[drive]
-            for part in disk.partitions:
-                if part.type in (parted.PARTITION_NORMAL,
-                                 parted.PARTITION_LOGICAL):
-                    device = part.getDeviceNodeName()
-                    if part.fileSystem:
-                        ptype = part.fileSystem.type
-                    else:
-                        ptype = None
-                    rc.append((device, ptype))
-
-        return rc
-
-    def diskState (self):
-        """Print out current disk state.  DEBUG."""
-        rc = ""
-        for disk in self.disks.values():
-            rc = rc + ("%s: %s length %ld, maximum "
-                       "primary partitions: %d\n" %
-                       (disk.device.path,
-                        disk.device.model,
-                        disk.device.length,
-                        disk.maxPrimaryPartitionCount))
-
-            for part in disk.partitions:
-                rc = rc + ("Device    Type         Filesystem   Start      "
-                           "End        Length        Flags\n")
-                rc = rc + ("------    ----         ----------   -----      "
-                           "---        ------        -----\n")
-                if not part.type & parted.PARTITION_METADATA:
-                    device = ""
-                    fs_type_name = ""
-                    if part.number > 0:
-                        device = part.getDeviceNodeName()
-                    if part.fileSystem:
-                        fs_type_name = part.fileSystem.type
-                    partFlags = part.getFlagsAsString()
-                    rc = rc + ("%-9s %-12s %-12s %-10ld %-10ld %-10ld %7s\n"
-                               % (device, part.type.name, fs_type_name,
-                              part.geometry.start, part.geometry.end,
-                              part.geometry.length, partFlags))
-
-        return rc
-
     def checkNoDisks(self):
         """Check that there are valid disk devices."""
         if len(self.disks.keys()) == 0:
@@ -1170,23 +672,4 @@
         return False
 
 
-    def exceptionDisks(self, anaconda, probe=True):
-        if probe:
-            isys.flushDriveDict()
-            self.refreshDevices()
-
-        drives = []
-        for d in isys.removableDriveDict().items():
-            func = lambda p: p.active and not p.getFlag(parted.PARTITION_RAID) and not p.getFlag(parted.PARTITION_LVM) and p.fileSystem.type in ["ext3", "ext2", "fat16", "fat32"]
-
-            disk = self.disks[d[0]]
-            parts = filter_partitions(disk, func)
-
-            if len(parts) == 0:
-                drives.append(d)
-            else:
-                for part in parts:
-                    name = "%s%s" % (part.disk.device.path, part.number)
-                    drives.append((os.path.basename(name), d[1]))
 
-        return drives
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/partIntfHelpers.py anaconda-storage/partIntfHelpers.py
--- anaconda/partIntfHelpers.py	2009-02-13 23:03:10.000000000 -0600
+++ anaconda-storage/partIntfHelpers.py	2009-02-15 22:46:46.000000000 -0600
@@ -26,11 +26,8 @@
 
 import string
 from constants import *
-import partedUtils
 import parted
-import fsset
 import iutil
-import partRequests
 
 import gettext
 _ = lambda x: gettext.ldgettext("anaconda", x)
@@ -106,222 +103,70 @@
         else:
             return None
     else:
-        if (fstype and fstype.isMountable() and (not preexisting or format)):
+        if (fstype and fstype.mountable and (not preexisting or format)):
             return _("Please specify a mount point for this partition.")
         else:
             # its an existing partition so don't force a mount point
             return None
 
-def isNotChangable(request, requestlist):
-    if request:
-        if requestlist.isRaidMember(request):
-	    parentreq = requestlist.getRaidMemberParent(request)
-	    if parentreq.raidminor is not None:
-		return _("This partition is part of "
-			 "the RAID device /dev/md%s.") % (parentreq.raidminor,)
-	    else:
-		return _("This partition is part of a RAID device.")
-
-	if requestlist.isLVMVolumeGroupMember(request):
-	    parentreq = requestlist.getLVMVolumeGroupMemberParent(request)
-	    if parentreq.volumeGroupName is not None:
-		return _("This partition is part of the "
-			 "LVM volume group '%s'.") % (parentreq.volumeGroupName,)
-	    else:
-		return _("This partition is part of a LVM volume group.")
-
-    return None
-    
-
-def doDeletePartitionByRequest(intf, requestlist, partition,
+def doDeletePartitionByRequest(intf, storage, partition,
                                confirm=1, quiet=0):
     """Delete a partition from the request list.
 
     intf is the interface
-    requestlist is the list of requests
+    devicetree is the device tree
     partition is either the part object or the uniqueID if not a part
     """
     
-    if partition == None:
+    if not partition:
         intf.messageWindow(_("Unable To Delete"),
                            _("You must first select a partition to delete."),
 			   custom_icon="error")
         return 0
 
-    if type(partition) == type("RAID"):
-        device = partition
-    elif partition.type & parted.PARTITION_FREESPACE:
-        intf.messageWindow(_("Unable To Delete"),
-                           _("You cannot delete free space."),
-			   custom_icon="error")
-        return 0
-    elif partition.type & parted.PARTITION_PROTECTED:
-        # LDL formatted DASDs always have one partition, you'd have to reformat the
-        # DASD in CDL mode to get rid of it
+    reason = storage.deviceImmutable(partition)
+    if reason:
         intf.messageWindow(_("Unable To Delete"),
-                           _("You cannot delete a partition of a LDL formatted DASD."),
-			   custom_icon="error")
+                           reason,
+                           customer_icon="error")
         return 0
-    else:
-        device = partition.getDeviceNodeName()
 
-    ret = requestlist.containsImmutablePart(partition)
-    if ret:
-        if not quiet:
-            intf.messageWindow(_("Unable To Delete"),
-                               _("You cannot delete this "
-                                 "partition, as it is an extended partition "
-                                 "which contains %s") %(ret),
-			       custom_icon="error")
+    if confirm and not confirmDeleteRequest(intf, partition):
         return 0
 
-    # see if device is in our partition requests, remove
-    if type(partition) == type("RAID"):
-	request = requestlist.getRequestByID(device)
-    else:
-	request = requestlist.getRequestByDeviceName(device)
-	    
-    if request:
-	state = isNotChangable(request, requestlist)
-
-        # If the partition is protected, we also can't delete it so specify a
-        # reason why.
-        if state is None and request.getProtected():
-            state = _("This partition is holding the data for the hard "
-                      "drive install.")
-
-        if state:
-            if not quiet:
-                intf.messageWindow(_("Unable To Delete"),
-				   _("You cannot delete this partition:\n\n") + state,
-				   custom_icon="error")
-	    return 0
-
-        if confirm and not confirmDeleteRequest(intf, request):
-            return 0
-
-        if request.getPreExisting():
-            if isinstance(request, partRequests.PartitionSpec):
-                # get the drive
-                drive = partedUtils.get_partition_drive(partition)
-
-                if partition.type & parted.PARTITION_EXTENDED:
-                    requestlist.deleteAllLogicalPartitions(partition)
-
-                delete = partRequests.DeleteSpec(drive,
-                                                 partition.geometry.start,
-                                                 partition.geometry.end)
-                requestlist.addDelete(delete)
-            elif isinstance(request, partRequests.LogicalVolumeRequestSpec):
-                vgreq = requestlist.getRequestByID(request.volumeGroup)
-                delete = partRequests.DeleteLogicalVolumeSpec(request.logicalVolumeName,
-                                                              vgreq.volumeGroupName)
-                requestlist.addDelete(delete)
-            elif isinstance(request, partRequests.VolumeGroupRequestSpec):
-                delete = partRequests.DeleteVolumeGroupSpec(request.volumeGroupName)
-                requestlist.addDelete(delete)
-            # FIXME: do we need to do anything with preexisting raids?
-
-        # now remove the request
-        requestlist.deleteDependentRequests(request)
-        requestlist.removeRequest(request)
-    else: # is this a extended partition we made?
-        if partition.type & parted.PARTITION_EXTENDED:
-            requestlist.deleteAllLogicalPartitions(partition)
-        else:
-            #raise ValueError, "Deleting a non-existent partition"
-            return 0
-
-    del partition
+    for dep in storage.deviceDeps(partition):
+        storage.deleteDevice(dep)
+    storage.deleteDevice(partition)
     return 1
 
-def doDeletePartitionsByDevice(intf, requestlist, diskset, device,
+def doDeletePartitionsByDevice(intf, storage, name,
 			       confirm=1, quiet=0):
     """ Remove all partitions currently on device """
     if confirm:
 	rc = intf.messageWindow(_("Confirm Delete"),
 				_("You are about to delete all partitions on "
-				  "the device '/dev/%s'.") % (device,),
+				  "the device '/dev/%s'.") % (name,),
 				type="custom", custom_icon="warning",
 				custom_buttons=[_("Cancel"), _("_Delete")])
 
 	if not rc:
 	    return
 
-    requests = requestlist.getRequestsByDevice(diskset, device)
-    if not requests:
+    device = storage.devicetree.getDeviceByName(name)
+    devices = storage.deviceDeps(device)
+    if not devices:
         return
 
-    # get list of unique IDs of these requests
-    reqIDs = set()
-    reqparts = {}
-
-    for req in requests:
-        for drive in req.drive:
-            part = diskset.disks[drive].getPartitionByPath("/dev/%s" % req.device)
-
-            if part.type & parted.PARTITION_FREESPACE or \
-               part.type & parted.PARTITION_METADATA or \
-               part.type & parted.PARTITION_PROTECTED:
-                continue
-
-            reqIDs.add(req.uniqueID)
-
-            if reqparts.has_key(req.uniqueID):
-                reqparts[req.uniqueID].append(part)
-            else:
-                reqparts[req.uniqueID] = [ part ]
-
-    reqIDs = list(reqIDs)
-
-    # now go thru and try to delete the unique IDs
-    for id in reqIDs:
-        try:
-            req = requestlist.getRequestByID(id)
-            if req is None:
-                continue
-            for partlist in reqparts[id]:
-                for part in partlist:
-                    rc = doDeletePartitionByRequest(intf, requestlist, part,
-                                                    confirm=0, quiet=1)
-                    if not rc:
-                        pass
-        except:
-            pass
-
-    # see which partitions are left
-    notdeleted = []
-    left_requests = requestlist.getRequestsByDevice(diskset, device)
-    if left_requests:
-        # get list of unique IDs of these requests
-        leftIDs = set()
-
-        for req in left_requests:
-            for drive in req.drive:
-                part = diskset.disks[drive].getPartitionByPath("/dev/%s" % req.device)
-
-                if part.type & parted.PARTITION_FREESPACE or \
-                   part.type & parted.PARTITION_METADATA or \
-                   part.type & parted.PARTITION_PROTECTED:
-                    continue
-
-                leftIDs.add(req.uniqueID)
-
-        leftIDs = list(leftIDs)
-
-        for id in leftIDs:
-            req = requestlist.getRequestByID(id)
-            notdeleted.append(req)
-
-    # see if we need to report any failures - some were because we removed
-    # an extended partition which contained other members of our delete list
-    outlist = ""
-    for req in notdeleted:
-        newreq = requestlist.getRequestByID(req.uniqueID)
-        if newreq:
-            outlist = outlist + "\t/dev/%s\n" % (newreq.device,)
+    immmutable = []
+    for dep in devices:
+        if not storage.deviceImmutable(dep):
+            immutable.append(dep.path)
+            continue
+
+        storage.deleteDevice(dep)
 
-    if outlist != "" and not quiet:
+    if immutable and not quiet:
+        remaining = "\n\t" + "\n\t".join(immutable) + "\n"
         intf.messageWindow(_("Notice"),
                            _("The following partitions were not deleted "
                              "because they are in use:\n\n%s") % outlist,
@@ -330,7 +175,7 @@
     return 1
 
 
-def doEditPartitionByRequest(intf, requestlist, part):
+def doEditPartitionByRequest(intf, storage, part):
     """Edit a partition from the request list.
 
     intf is the interface
@@ -340,97 +185,56 @@
     
     if part == None:
         intf.messageWindow(_("Unable To Edit"),
-                           _("You must select a partition to edit"), custom_icon="error")
+                           _("You must select a partition to edit"),
+                           custom_icon="error")
 
         return (None, None)
 
-    if type(part) == type("RAID"):
-
-	# see if device is in our partition requests, remove
-        request = requestlist.getRequestByID(int(part))
-	    
-	if request:
-	    state = isNotChangable(request, requestlist)
-	    if state is not None:
-		intf.messageWindow(_("Unable To Edit"), _("You cannot edit this partition:\n\n") + state,
-				   custom_icon="error")
-		return (None, None)
-
-	if request.type == REQUEST_RAID:
-	    return ("RAID", request)
-	elif request.type == REQUEST_VG:
-	    return ("LVMVG", request)
-	elif request.type == REQUEST_LV:
-	    return ("LVMLV", request)
-	else:
-	    return (None, None)
-    elif part.type & parted.PARTITION_FREESPACE:
-        request = partRequests.PartitionSpec(fsset.fileSystemTypeGetDefault(),
-            start = part.geometry.device.startSectorToCylinder(part.geometry.start),
-            end = part.geometry.device.endSectorToCylinder(part.geometry.end),
-            drive = [ partedUtils.get_partition_drive(part) ])
-
-        return ("NEW", request)
-    elif part.type & parted.PARTITION_EXTENDED:
-        return (None, None)
+    if isinstance(part, str):
+        device = storage.devicetree.getDeviceByName(part)
+    else:
+        device = part
 
-    ret = requestlist.containsImmutablePart(part)
-    if ret:
+    reason = storage.deviceImmutable(device)
+    if reason:
         intf.messageWindow(_("Unable To Edit"),
-                           _("You cannot edit this "
-                             "partition, as it is an extended partition "
-                             "which contains %s") %(ret), custom_icon="error")
+                           _("You cannot edit this device:\n\n")
+                           reason,
+                           custom_icon="error")
         return (None, None)
 
-    name = part.getDeviceNodeName()
-    request = requestlist.getRequestByDeviceName(name)
-    if request:
-	state = isNotChangable(request, requestlist)
-	if state is not None:
-	    intf.messageWindow(_("Unable To Edit"),
-			       _("You cannot edit this partition:\n\n") + state, custom_icon="error")
-	    return (None, None)
-	
-        return ("PARTITION", request)
-    else: # shouldn't ever happen
-        raise ValueError, ("Trying to edit non-existent partition %s"
-                           % (part.getDeviceNodeName(),))
-
+    if device.type == "mdarray":
+        return ("RAID", device)
+    elif device.type == "lvmvg":
+        return ("LVMVG", device)
+    elif device.type == "lvmlv":
+        return ("LVMLV", device)
+    elif device.type == "partition":
+        return ("PARTITION", device)
+    else:
+        return (None, None)
 
 def checkForSwapNoMatch(anaconda):
     """Check for any partitions of type 0x82 which don't have a swap fs."""
-    diskset = anaconda.id.diskset
-
-    for request in anaconda.id.partitions.requests:
-        if not request.dev or not request.fstype:
+    for device in anaconda.id.storage.partitions:
+        if not device.exists:
+            # this is only for existing partitions
             continue
 
-        for drive in request.drive:
-            part = diskset.disks[drive].getPartitionByPath("/dev/%s" % request.device)
-
-            if (part and (not part.type & parted.PARTITION_FREESPACE)
-                and (part.getFlag(parted.PARTITION_SWAP))
-                and (request.fstype and request.fstype.getName() != "swap")
-                and (not request.format)):
-                rc = anaconda.intf.messageWindow(_("Format as Swap?"),
-                                        _("/dev/%s has a partition type of 0x82 "
-                                          "(Linux swap) but does not appear to "
-                                          "be formatted as a Linux swap "
-                                          "partition.\n\n"
-                                          "Would you like to format this "
-                                          "partition as a swap partition?")
-                                        % (request.device), type = "yesno",
-                                        custom_icon="question")
-                if rc == 1:
-                    request.format = 1
-                    request.fstype = fsset.fileSystemTypeGet("swap")
-                    if request.fstype.getName() == "software RAID":
-                        part.setFlag(parted.PARTITION_RAID)
-                    else:
-                        part.unsetFlag(parted.PARTITION_RAID)
-
-                    partedUtils.set_partition_file_system_type(part,
-                                                               request.fstype)
+        if device.partType & parted.PARTITION_SWAP and \
+           not device.format.type == "swap":
+            rc = anaconda.intf.messageWindow(_("Format as Swap?"),
+                                    _("%s has a partition type of 0x82 "
+                                      "(Linux swap) but does not appear to "
+                                      "be formatted as a Linux swap "
+                                      "partition.\n\n"
+                                      "Would you like to format this "
+                                      "partition as a swap partition?")
+                                    % device.path, type = "yesno",
+                                    custom_icon="question")
+            if rc == 1:
+                format = getFormat("swap", device=device.path)
+                anaconda.id.storage.formatDevice(device, format)
 
 def mustHaveSelectedDrive(intf):
     txt =_("You need to select at least one hard drive to install %s.") % (productName,)
@@ -498,63 +302,42 @@
                                 type="yesno", custom_icon="warning")
     return rc
 
-def getPreExistFormatWarnings(partitions, diskset):
-    """Return a list of preexisting partitions being formatted."""
-
-    devs = []
-    for request in partitions.requests:
-        if request.getPreExisting() == 1:
-            devs.append(request.uniqueID)
+def getPreExistFormatWarnings(storage):
+    """Return a list of preexisting devices being formatted."""
+    devices = []
+    for device in storage.devicetree.devices.values():
+        if device.exists and not device.format.exists:
+            devices.append(device)
 
-    devs.sort()
-    
+    devices.sort(key=lambda d: d.name)
     rc = []
-    for dev in devs:
-        request = partitions.getRequestByID(dev)
-        if request.format:
-            if request.fstype.isMountable():
-                mntpt = request.mountpoint
-            else:
-                mntpt = ""
-
-            if isinstance(request, partRequests.PartitionSpec):
-                dev = request.device
-            elif isinstance(request, partRequests.RaidRequestSpec):
-                dev = "md%s" %(request.raidminor,)
-            elif isinstance(request, partRequests.VolumeGroupRequestSpec):
-                dev = request.volumeGroupName
-            elif isinstance(request, partRequests.LogicalVolumeRequestSpec):
-                vgreq = partitions.getRequestByID(request.volumeGroup)
-                dev = "%s/%s" %(vgreq.volumeGroupName,
-                                request.logicalVolumeName)
-
-            rc.append((dev, request.fstype.getName(), mntpt))
-
-    if len(rc) == 0:
-        return None
-    else:
-        return rc
+    for device in devices:
+        rc.append((device.path,
+                   device.format.type,
+                   getattr(device.format, "mountpoint", "")))
+    return rc
             
-def confirmDeleteRequest(intf, request):
-    """Confirm the deletion of a request."""
-    if not request:
+def confirmDeleteRequest(intf, device):
+    """Confirm the deletion of a new device."""
+    if not device:
 	return
     
-    if request.type == REQUEST_VG:
+    if device.type == "lvmvg":
 	errmsg = (_("You are about to delete the volume group \"%s\"."
                     "\n\nALL logical volumes in this volume group "
-                    "will be lost!") % (request.volumeGroupName,))
-    elif request.type == REQUEST_LV:
+                    "will be lost!") % device.name)
+    elif device.type == "lvmlv":
 	errmsg = (_("You are about to delete the logical volume \"%s\".")
-                  % (request.logicalVolumeName,))
-    elif request.type == REQUEST_RAID:
+                  % (device.name)
+    elif device.type == "mdarray":
 	errmsg = _("You are about to delete a RAID device.")
+    elif device.type == "partition":
+	errmsg = (_("You are about to delete the %s partition.")
+                  % device.path)
     else:
-	if request.device:
-	    errmsg = _("You are about to delete the /dev/%s partition.") % (request.device,)
-	else:
-	    # XXX can this ever happen?
-	    errmsg = _("The partition you selected will be deleted.")
+        # we may want something a little bit prettier than device.type
+        errmsg = (_("You are about to delete the %s %s") % (device.type,
+                                                            device.name))
 
     rc = intf.messageWindow(_("Confirm Delete"), errmsg, type="custom",
 				custom_buttons=[_("Cancel"), _("_Delete")],
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/partitions.py anaconda-storage/partitions.py
--- anaconda/partitions.py	2009-02-13 23:03:10.000000000 -0600
+++ anaconda-storage/partitions.py	1969-12-31 18:00:00.000000000 -0600
@@ -1,1899 +0,0 @@
-#
-# partitions.py: partition object containing partitioning info
-#
-# Copyright (C) 2002, 2003, 2004, 2005, 2006  Red Hat, Inc.
-# All rights reserved.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-#
-# Author(s): Matt Wilson <msw@redhat.com>
-#            Jeremy Katz <katzj@redhat.com>
-#            Mike Fulbright <msf@redhat.com>
-#            Harald Hoyer <harald@redhat.de>
-#
-
-"""Overarching partition object."""
-
-import parted
-import iutil
-import isys
-import string
-import os
-import sys
-
-from constants import *
-from flags import flags
-from errors import *
-
-import fsset
-import raid
-import lvm
-import partedUtils
-import partRequests
-import cryptodev
-
-import gettext
-_ = lambda x: gettext.ldgettext("anaconda", x)
-
-import logging
-log = logging.getLogger("anaconda")
-
-# dispatch.py helper function
-def partitionObjectsInitialize(anaconda):
-    # shut down all dm devices
-    anaconda.id.diskset.closeDevices()
-    anaconda.id.diskset.stopMdRaid()
-    anaconda.id.zfcp.shutdown()
-
-    # clean slate about drives
-    isys.flushDriveDict()
-
-    if anaconda.dir == DISPATCH_BACK:
-        return
-
-    # make ibft configured iscsi disks available when findrootparts was skipped
-    anaconda.id.iscsi.startup(anaconda.intf)
-
-    # ensure zfcp devs are up
-    anaconda.id.zfcp.startup()
-
-    # pull in the new iscsi drive
-    isys.flushDriveDict()
-
-    # read in drive info
-    anaconda.id.diskset.refreshDevices()
-
-    anaconda.id.partitions.setFromDisk(anaconda.id.diskset)
-    anaconda.id.partitions.setProtected(anaconda.dispatch)
-
-# dispatch.py helper function
-def partitioningComplete(anaconda):
-    if anaconda.dir == DISPATCH_BACK and anaconda.id.fsset.isActive():
-        rc = anaconda.intf.messageWindow(_("Installation cannot continue."),
-                                _("The partitioning options you have chosen "
-                                  "have already been activated. You can "
-                                  "no longer return to the disk editing "
-                                  "screen. Would you like to continue "
-                                  "with the installation process?"),
-                                type = "yesno")
-        if rc == 0:
-            sys.exit(0)
-        return DISPATCH_FORWARD
-
-    anaconda.id.partitions.sortRequests()
-    anaconda.id.fsset.reset()
-    undoEncryption = False
-    partitions = anaconda.id.partitions
-    preexist = partitions.hasPreexistingCryptoDev()
-    for request in anaconda.id.partitions.requests:
-        # XXX improve sanity checking
-        if (not request.fstype or (request.fstype.isMountable()
-            and not request.mountpoint)):
-            continue
-
-        if request.encryption and request.encryption.format:
-            if anaconda.isKickstart and request.encryption.hasPassphrase():
-                # they set a passphrase for this device explicitly
-                pass
-            elif partitions.encryptionPassphrase:
-                request.encryption.setPassphrase(partitions.encryptionPassphrase)
-            elif undoEncryption:
-                request.encryption = None
-                if request.dev:
-                    request.dev.crypto = None
-            else:
-                while True:
-                    (passphrase, retrofit) = anaconda.intf.getLuksPassphrase(preexist=preexist)
-                    if passphrase:
-                        request.encryption.setPassphrase(passphrase)
-                        partitions.encryptionPassphrase = passphrase
-                        partitions.retrofitPassphrase = retrofit
-                        break
-                    else:
-                        rc = anaconda.intf.messageWindow(_("Encrypt device?"),
-                                    _("You specified block device encryption "
-                                      "should be enabled, but you have not "
-                                      "supplied a passphrase. If you do not "
-                                      "go back and provide a passphrase, "
-                                      "block device encryption will be "
-                                      "disabled."),
-                                      type="custom",
-                                      custom_buttons=[_("Back"), _("Continue")],
-                                      default=0)
-                        if rc == 1:
-                            log.info("user elected to not encrypt any devices.")
-                            request.encryption = None
-                            if request.dev:
-                                request.dev.encryption = None
-                            undoEncryption = True
-                            partitions.autoEncrypt = False
-                            break
-
-        entry = request.toEntry(anaconda.id.partitions)
-        if entry:
-            anaconda.id.fsset.add (entry)
-        else:
-            raise RuntimeError, ("Managed to not get an entry back from "
-                                 "request.toEntry")
-
-    if anaconda.isKickstart:
-        return
-
-    rc = anaconda.intf.messageWindow(_("Writing partitioning to disk"),
-                                _("The partitioning options you have selected "
-                                  "will now be written to disk.  Any "
-                                  "data on deleted or reformatted partitions "
-                                  "will be lost."),
-                                type = "custom", custom_icon="warning",
-                                custom_buttons=[_("Go _back"),
-                                                _("_Write changes to disk")],
-                                default = 0)
-    if rc == 0:
-        return DISPATCH_BACK
-
-def lookup_cryptodev(device):
-    for encryptedDev, cdev in Partitions.encryptedDevices.items():
-        mappedDev = cdev.getDevice()
-        if device == encryptedDev or device == mappedDev:
-            return cdev
-
-class Partitions:
-    """Defines all of the partition requests and delete requests."""
-    encryptedDevices = {}
-
-    def __init__ (self, anaconda, readDisks=False):
-        """Initializes a Partitions object.
-
-        Can pass in the diskset if it already exists.
-        """
-        self.anaconda = anaconda
-
-        self.requests = []
-        """A list of RequestSpec objects for all partitions."""
-
-        self.deletes = []
-        """A list of DeleteSpec objects for partitions to be deleted."""
-
-        self.autoPartitionRequests = []
-        """A list of RequestSpec objects for autopartitioning.
-        These are setup by the installclass and folded into self.requests
-        by auto partitioning."""
-
-        self.autoClearPartType = CLEARPART_TYPE_NONE
-        """What type of partitions should be cleared?"""
-
-        self.autoClearPartDrives = None
-        """Drives to clear partitions on (note that None is equiv to all)."""
-
-        self.nextUniqueID = 1
-        """Internal counter.  Don't touch unless you're smarter than me."""
-
-        self.reinitializeDisks = 0
-        """Should the disk label be reset on all disks?"""
-
-        self.zeroMbr = 0
-        """Should the mbr be zero'd?"""
-
-        self.protected = []
-        """A list of partitions that are the installation source for hard
-           drive or livecd installs.  Partitions on this list may not be
-           formatted."""
-
-        self.autoEncrypt = False
-
-        self.encryptionPassphrase = ""
-        self.retrofitPassphrase = False
-
-        # partition method to be used.  not to be touched externally
-        self.useAutopartitioning = 1
-        self.useFdisk = 0
-
-        # autopartitioning info becomes kickstart partition requests
-        # and its useful to be able to differentiate between the two
-        self.isKickstart = 0
-
-        if readDisks:
-            self.anaconda.id.diskset.refreshDevices()
-            self.setFromDisk(self.anaconda.id.diskset)
-
-    def protectedPartitions(self):
-        return self.protected
-
-    def hasPreexistingCryptoDev(self):
-        rc = False
-        for request in self.requests:
-            if request.encryption and request.encryption.format == 0:
-                rc = True
-                break
-
-        return rc
-
-    def getCryptoDev(self, device):
-        log.info("going to get passphrase for encrypted device %s" % device)
-        luksDev = self.encryptedDevices.get(device)
-        if luksDev:
-            log.debug("passphrase for device %s already known" % device)
-            return luksDev
-
-        intf = self.anaconda.intf
-        luksDev = cryptodev.LUKSDevice(device)
-        if self.encryptionPassphrase:
-            luksDev.setPassphrase(self.encryptionPassphrase)
-            if not luksDev.openDevice():
-                self.encryptedDevices[device] = luksDev
-                return luksDev
-            else:
-                luksDev.setPassphrase("")
-
-        if intf is None:
-            return
-
-        buttons = [_("Back"), _("Continue")]
-        devname = os.path.basename(device)
-        while True:
-            (passphrase, isglobal) = intf.passphraseEntryWindow(devname)
-            if not passphrase:
-                rc = intf.messageWindow(_("Confirm"),
-                                        _("Are you sure you want to skip "
-                                          "entering a passphrase for device "
-                                          "%s?\n\n"
-                                          "If you skip this step the "
-                                          "device's contents will not "
-                                          "be available during "
-                                          "installation.") % devname,
-                                        type = "custom",
-                                        default = 0,
-                                        custom_buttons = buttons)
-                if rc == 0:
-                    continue
-                else:
-                    log.info("skipping passphrase for %s" % (device,))
-                    break
-
-            luksDev.setPassphrase(passphrase)
-            rc = luksDev.openDevice()
-            if rc:
-                luksDev.setPassphrase("")
-                continue
-            else:
-                self.encryptedDevices[device] = luksDev
-                if isglobal:
-                    self.encryptionPassphrase = passphrase
-                break
-
-        return self.encryptedDevices.get(device)
-
-    def getEncryptedDevices(self, diskset):
-        """ find and obtain passphrase for any encrypted devices """
-        drives = diskset.disks.keys()
-        drives.sort()
-        for drive in drives:
-            if diskset.anaconda.isKickstart and \
-               ((self.autoClearPartType != CLEARPART_TYPE_NONE and \
-                 (not self.autoClearPartDrives or \
-                  drive in self.autoClearPartDrives)) or \
-                 drive in diskset.skippedDisks):
-                continue
-
-            disk = diskset.disks[drive]
-            for part in disk.partitions:
-                if part.type & parted.PARTITION_METADATA:
-                    continue
-
-                device = part.getDeviceNodeName()
-                fs = isys.readFSType("/dev/%s" % (device,))
-                if fs and fs.endswith("raid"):
-                    continue
-
-                if cryptodev.isLuks("/dev/%s" % device):
-                    self.getCryptoDev(device)
-
-        diskset.startMPath()
-        diskset.startDmRaid()
-        diskset.startMdRaid()
-        mdList = diskset.mdList
-        for raidDev in mdList:
-            (theDev, devices, level, numActive) = raidDev
-            if cryptodev.isLuks("/dev/%s" % theDev):
-                self.getCryptoDev(theDev)
-
-        lvm.writeForceConf()
-        # now to read in pre-existing LVM stuff
-        lvm.vgscan()
-        lvm.vgactivate()
-
-        for (vg, size, pesize, vgfree) in lvm.vglist():
-            for (lvvg, lv, size, lvorigin) in lvm.lvlist():
-                if lvorigin:
-                    continue
-                if lvvg != vg:
-                    continue
-
-                theDev = "/dev/mapper/%s-%s" %(vg, lv)
-                if cryptodev.isLuks(theDev):
-                    self.getCryptoDev("mapper/%s-%s" % (vg, lv))
-
-        lvm.vgdeactivate()
-        diskset.stopMdRaid()
-        for luksDev in self.encryptedDevices.values():
-            luksDev.closeDevice()
-        # try again now that encryption mappings are closed
-        lvm.vgdeactivate()
-        diskset.stopMdRaid()
-        for luksDev in self.encryptedDevices.values():
-            luksDev.closeDevice()
-
-        # We shouldn't have any further need for the global passphrase
-        # except for new device creation, in which case we want to give
-        # the user a chance to establish a new global passphrase.
-        self.encryptionPassphrase = ""
-
-    def setFromDisk(self, diskset):
-        """Clear the delete list and set self.requests to reflect disk."""
-        self.deletes = []
-        self.requests = []
-        if diskset.anaconda.isKickstart and not diskset.anaconda.id.upgrade:
-            self.getEncryptedDevices(diskset)
-        labels = diskset.getInfo()
-        drives = diskset.disks.keys()
-        drives.sort()
-        for drive in drives:
-            disk = diskset.disks[drive]
-            part = disk.getFirstPartition()
-            while part:
-                if (part.type & parted.PARTITION_METADATA) or \
-                   (part.type & parted.PARTITION_FREESPACE) or \
-                   (part.type & parted.PARTITION_PROTECTED):
-                    part = part.nextPartition()
-                    continue
-
-                format = None
-                if part.type & parted.PARTITION_EXTENDED:
-                    ptype = None
-                elif part.getFlag(parted.PARTITION_RAID) == 1:
-                    ptype = fsset.fileSystemTypeGet("software RAID")
-                elif part.getFlag(parted.PARTITION_LVM) == 1:
-                    ptype = fsset.fileSystemTypeGet("physical volume (LVM)")
-                else:
-                    ptype = partedUtils.get_partition_file_system_type(part)
-
-                    # FIXME: we don't handle ptype being None very well, so
-                    # just say it's foreign.  Should probably fix None
-                    # handling instead some day.
-                    if ptype is None:
-                        ptype = fsset.fileSystemTypeGet("foreign")
-
-                device = part.getDeviceNodeName()
-
-                # parted doesn't tell ext4 from ext3
-                if ptype == fsset.fileSystemTypeGet("ext3"): 
-                    fsname = isys.readFSType("/dev/%s" % (device,))
-                    try:
-                        ptype = fsset.fileSystemTypeGet(fsname)
-                    except:
-                        ptype = fsset.fileSystemTypeGet("foreign")
-
-                luksDev = self.encryptedDevices.get(device)
-                if luksDev and not luksDev.openDevice():
-                    mappedDev = luksDev.getDevice()
-                    fsname = isys.readFSType("/dev/%s" % (mappedDev,))
-                    try:
-                        ptype = fsset.fileSystemTypeGet(fsname)
-                    except:
-                        ptype = fsset.fileSystemTypeGet("foreign")
-                elif cryptodev.isLuks("/dev/%s" % device):
-                    ptype = fsset.fileSystemTypeGet("foreign")
-
-                start = part.geometry.start
-                end = part.geometry.end
-                size = part.getSize(unit="MB")
-                drive = partedUtils.get_partition_drive(part)
-
-                spec = partRequests.PreexistingPartitionSpec(ptype,
-                                                             size = size,
-                                                             start = start,
-                                                             end = end,
-                                                             drive = drive,
-                                                             format = format)
-                spec.device = fsset.PartedPartitionDevice(part).getDevice()
-                spec.encryption = luksDev
-                spec.maxResizeSize = part.getMaxAvailableSize(unit="MB")
-
-                # set label if makes sense
-                if ptype and ptype.isMountable():
-                    if spec.device in labels.keys():
-                        if labels[spec.device] and len(labels[spec.device])>0:
-                            spec.fslabel = labels[spec.device]
-                    elif luksDev and not luksDev.getStatus() and mappedDev in labels.keys():
-                        if labels[mappedDev] and len(labels[mappedDev])>0:
-                            spec.fslabel = labels[mappedDev]
-                self.addRequest(spec)
-
-                part = part.nextPartition()
-
-        # now we need to read in all pre-existing RAID stuff
-        diskset.startMPath()
-        diskset.startDmRaid()
-        diskset.startMdRaid()
-        mdList = diskset.mdList
-        for raidDev in mdList:
-            (theDev, devices, level, numActive) = raidDev
-            level = "RAID%s" %(level,)
-
-            if level not in raid.availRaidLevels:
-                log.warning("raid level %s not supported, skipping %s" %(level,
-                                                                  theDev))
-                continue
-
-            try:
-                chunk = isys.getRaidChunkFromDevice("/dev/%s" %(devices[0],))
-            except Exception, e:
-                log.error("couldn't get chunksize of %s: %s" %(theDev, e))
-                chunk = None
-            
-            # is minor always mdN ?
-            minor = int(theDev[2:])
-            raidvols = []
-            for dev in devices:
-                req = self.getRequestByDeviceName(dev)
-                if not req:
-                    log.error("RAID device %s using non-existent partition %s"
-                              %(theDev, dev))
-                    continue
-                raidvols.append(req.uniqueID)
-                
-
-            luksDev = self.encryptedDevices.get(theDev)
-            if luksDev and not luksDev.openDevice():
-                device = luksDev.getDevice()
-            else:
-                device = theDev
-
-            fs = isys.readFSType("/dev/%s" %(device,))
-            try:
-                fsystem = fsset.fileSystemTypeGet(fs)
-            except:
-                fsystem = fsset.fileSystemTypeGet("foreign")
-
-            try:
-                fslabel = isys.readFSLabel(device)
-            except:
-                fslabel = None
-
-            mnt = None
-            format = 0
-                    
-            spares = len(devices) - numActive
-            spec = partRequests.RaidRequestSpec(fsystem, format = format,
-                                                raidlevel = level,
-                                                raidmembers = raidvols,
-                                                raidminor = minor,
-                                                raidspares = spares,
-                                                mountpoint = mnt,
-                                                preexist = 1,
-                                                chunksize = chunk,
-                                                fslabel = fslabel)
-            spec.size = spec.getActualSize(self, diskset)
-            spec.encryption = luksDev
-            self.addRequest(spec)
-
-        lvm.writeForceConf()
-        # now to read in pre-existing LVM stuff
-        lvm.vgscan()
-        lvm.vgactivate()
-
-        pvs = lvm.pvlist()
-        for (vg, size, pesize, vgfree) in lvm.vglist():
-            try:
-                preexist_size = float(size)
-            except:
-                log.error("preexisting size for %s not a valid integer, ignoring" %(vg,))
-                preexist_size = None
-
-            pvids = []
-            for (dev, pvvg, size) in pvs:
-                if vg != pvvg:
-                    continue
-                req = self.getRequestByDeviceName(dev[5:])
-                if not req:
-                    log.error("Volume group %s using non-existent partition %s"
-                              %(vg, dev))
-                    continue
-                pvids.append(req.uniqueID)
-            spec = partRequests.VolumeGroupRequestSpec(format = 0,
-                                                       vgname = vg,
-                                                       physvols = pvids,
-                                                       pesize = pesize,
-                                                       preexist = 1,
-                                                       preexist_size = preexist_size)
-            spec.free = vgfree
-            vgid = self.addRequest(spec)
-
-            for (lvvg, lv, size, lvorigin) in lvm.lvlist():
-                if lvorigin:
-                    continue
-                if lvvg != vg:
-                    continue
-                
-                # size is number of bytes, we want size in megs
-                lvsize = float(size)
-
-                theDev = "/dev/%s/%s" %(vg, lv)
-
-                luksDev = self.encryptedDevices.get("mapper/%s-%s" % (vg, lv))
-                if luksDev and not luksDev.openDevice():
-                    device = luksDev.getDevice()
-                else:
-                    device = theDev
-
-                fs = isys.readFSType(device)
-                fslabel = None
-
-                try:
-                    fsystem = fsset.fileSystemTypeGet(fs)
-                except:
-                    fsystem = fsset.fileSystemTypeGet("foreign")
-
-                try:
-                    fslabel = isys.readFSLabel(device)
-                except:
-                    fslabel = None
-
-                mnt = None
-                format = 0
-
-                spec = partRequests.LogicalVolumeRequestSpec(fsystem,
-                    format = format, size = lvsize, volgroup = vgid,
-                    lvname = lv, mountpoint = mnt, fslabel = fslabel,
-                    preexist = 1)
-                if fsystem.isResizable():
-                    spec.minResizeSize = fsystem.getMinimumSize("%s/%s" %(vg, lv))
-                spec.encryption = luksDev
-                self.addRequest(spec)
-
-        for vg in lvm.partialvgs():
-            spec = partRequests.PartialVolumeGroupSpec(vgname = vg)
-            self.addDelete(spec)
-            
-        lvm.vgdeactivate()
-        diskset.stopMdRaid()
-        for luksDev in self.encryptedDevices.values():
-            luksDev.closeDevice()
-
-        # try again now that encryption mappings are closed
-        lvm.vgdeactivate()
-        diskset.stopMdRaid()
-        for luksDev in self.encryptedDevices.values():
-            luksDev.closeDevice()
-
-    def addRequest (self, request):
-        """Add a new request to the list."""
-        if not request.uniqueID:
-            request.uniqueID = self.nextUniqueID
-            self.nextUniqueID = self.nextUniqueID + 1
-        self.requests.append(request)
-        self.requests.sort()
-
-        return request.uniqueID
-
-    def addDelete (self, delete):
-        """Add a new DeleteSpec to the list."""
-        self.deletes.append(delete)
-        self.deletes.sort()
-
-    def removeRequest (self, request):
-        """Remove a request from the list."""
-        self.requests.remove(request)
-
-    def getRequestByMountPoint(self, mount):
-        """Find and return the request with the given mountpoint."""
-        for request in self.requests:
-            if request.mountpoint == mount:
-                return request
-	    
-	for request in self.requests:
-	    if request.type == REQUEST_LV and request.mountpoint == mount:
-		return request
-        return None
-
-    def getRequestByDeviceName(self, device):
-        """Find and return the request with the given device name."""
-	if device is None:
-	    return None
-	
-        for request in self.requests:
-	    if request.type == REQUEST_RAID and request.raidminor is not None:
-		tmp = "md%d" % (request.raidminor,)
-		if tmp == device:
-		    return request
-	    elif request.device == device:
-                return request
-            elif request.encryption:
-                deviceUUID = cryptodev.luksUUID("/dev/" + device)
-                cryptoDev = request.encryption.getDevice()
-                cryptoUUID = request.encryption.getUUID()
-                if cryptoDev == device or \
-                   (cryptoUUID and cryptoUUID == deviceUUID):
-                    return request
-        return None
-
-
-    def getRequestsByDevice(self, diskset, device):
-        """Find and return the requests on a given device (like 'hda')."""
-        if device is None:
-            return None
-
-        drives = diskset.disks.keys()
-        if device not in drives:
-            return None
-
-        rc = []
-        disk = diskset.disks[device]
-        for part in disk.partitions:
-            dev = part.getDeviceNodeName()
-            request = self.getRequestByDeviceName(dev)
-
-            if request:
-                rc.append(request)
-
-        if len(rc) > 0:
-            return rc
-        else:
-            return None
-
-    def getRequestByVolumeGroupName(self, volname):
-        """Find and return the request with the given volume group name."""
-	if volname is None:
-	    return None
-	
-	for request in self.requests:
-	    if (request.type == REQUEST_VG and
-                request.volumeGroupName == volname):
-		return request
-        return None
-
-    def getRequestByLogicalVolumeName(self, lvname):
-        """Find and return the request with the given logical volume name."""
-	if lvname is None:
-	    return None
-	for request in self.requests:
-	    if (request.type == REQUEST_LV and
-                request.logicalVolumeName == lvname):
-		return request
-        return None
-
-    def getRequestByID(self, id):
-        """Find and return the request with the given unique ID.
-
-        Note that if id is a string, it will be converted to an int for you.
-        """
-	if type(id) == type("a string"):
-	    id = int(id)
-        for request in self.requests:
-            if request.uniqueID == id:
-                return request
-        return None
-
-    def getUnderlyingRequests(self, request):
-        loop = True
-        requests = [ ]
-        requests.append(request)
-
-        # loop over requests descending the storage stack until we
-        # are at the bottom
-        while loop:
-            loop_requests = requests
-            requests = [ ]
-            loop = False
-
-            for request in loop_requests:
-                ids = [ ]
-
-                if (request.type == REQUEST_NEW or \
-                    request.type == REQUEST_PREEXIST):
-                    requests.append(request)
-                elif (request.type == REQUEST_RAID):
-                    if request.raidmembers:
-                        ids = request.raidmembers
-                    else:
-                        requests.append(request)
-                elif (request.type == REQUEST_VG):
-                    ids = request.physicalVolumes
-                elif (request.type == REQUEST_LV):
-                    ids.append(request.volumeGroup)
-                else:
-                    log.error("getUnderlyingRequests unknown request type")
-
-                for id in ids:
-                    tmpreq = self.getRequestByID(id)
-                    if tmpreq:
-                        requests.append(tmpreq)
-                        loop = True
-                    else:
-                        log.error("getUnderlyingRequests could not get request for id %s" % (id,))
-
-        return requests
-
-    def getRaidRequests(self):
-        """Find and return a list of all of the RAID requests."""
-        retval = []
-        for request in self.requests:
-            if request.type == REQUEST_RAID:
-                retval.append(request)
-
-        return retval
-
-    def getRaidDevices(self):
-        """Find and return a list of all of the requests for use in RAID."""
-        raidRequests = []
-        for request in self.requests:
-            if isinstance(request, partRequests.RaidRequestSpec):
-                raidRequests.append(request)
-                
-        return raidRequests
-
-    def getAvailableRaidMinors(self):
-        """Find and return a list of all of the unused minors for use in RAID."""
-        raidMinors = range(0,32)
-        for request in self.requests:
-            if isinstance(request, partRequests.RaidRequestSpec) and request.raidminor in raidMinors:
-                raidMinors.remove(request.raidminor)
-                
-        return raidMinors
-	
-
-    def getAvailRaidPartitions(self, request, diskset):
-        """Return a list of tuples of RAID partitions which can be used.
-
-        Return value is (part, size, used) where used is 0 if not,
-        1 if so, 2 if used for *this* request.
-        """
-        rc = []
-        drives = diskset.disks.keys()
-        raiddevs = self.getRaidDevices()
-        drives.sort()
-        for drive in drives:
-            disk = diskset.disks[drive]
-            for part in disk.getRaidPartitions():
-                partname = part.getDeviceNodeName()
-                used = 0
-                for raid in raiddevs:
-                    if raid.raidmembers:
-                        for raidmem in raid.raidmembers:
-                            tmpreq = self.getRequestByID(raidmem)
-                            if (partname == tmpreq.device):
-                                if raid.uniqueID == request.uniqueID:
-                                    used = 2
-                                else:
-                                    used = 1
-                                break
-                    if used:
-                        break
-                size = part.getSize(unit="MB")
-
-                if not used:
-                    rc.append((partname, size, 0))
-                elif used == 2:
-                    rc.append((partname, size, 1))
-		    
-        return rc
-
-    def getRaidMemberParent(self, request):
-        """Return RAID device request containing this request."""
-        raiddev = self.getRaidRequests()
-        if not raiddev or not request.device:
-            return None
-        for dev in raiddev:
-            if not dev.raidmembers:
-                continue
-            for member in dev.raidmembers:
-                if request.device == self.getRequestByID(member).device:
-                    return dev
-        return None
-
-    def isRaidMember(self, request):
-        """Return whether or not the request is being used in a RAID device."""
-	if self.getRaidMemberParent(request) is not None:
-	    return 1
-	else:
-	    return 0
-
-    def getLVMLVForVGID(self, vgid):        
-        """Find and return a list of all the LVs associated with a VG id."""
-        retval = []
-        for request in self.requests:
-	    if request.type == REQUEST_LV:
-		if request.volumeGroup == vgid:
-                    retval.append(request)
-        return retval
-
-    def getLVMLVForVG(self, vgrequest):
-        """Find and return a list of all of the LVs in the VG."""
-        vgid = vgrequest.uniqueID
-        return self.getLVMLVForVGID(vgid)
-		
-    def getLVMRequests(self):
-        """Return a dictionary of all of the LVM bits.
-
-        The dictionary returned is of the form vgname: [ lvrequests ]
-        """
-        retval = {}
-        for request in self.requests:
-            if request.type == REQUEST_VG:
-                retval[request.volumeGroupName] = self.getLVMLVForVG(request)
-	    
-        return retval
-
-    def getPartialLVMRequests(self):
-        """Return a list of all of the partial volume groups names."""
-        retval = []
-        for request in self.deletes:
-            if isinstance(request, partRequests.PartialVolumeGroupSpec):
-                retval.append(request.volumeGroupName)
-	    
-        return retval
-
-    def getLVMVGRequests(self):
-        """Find and return a list of all of the volume groups."""
-        retval = []
-        for request in self.requests:
-            if request.type == REQUEST_VG:
-                retval.append(request)
-
-        return retval
-
-    def getLVMLVRequests(self):
-        """Find and return a list of all of the logical volumes."""
-        retval = []
-        for request in self.requests:
-            if request.type == REQUEST_LV:
-                retval.append(request)
-
-        return retval
-
-    def getAvailLVMPartitions(self, request, diskset):
-        """Return a list of tuples of PV partitions which can be used.
-
-        Return value is (part, size, used) where used is 0 if not,
-        1 if so, 2 if used for *this* request.
-        """
-        rc = []
-        drives = diskset.disks.keys()
-        drives.sort()
-        volgroups = self.getLVMVGRequests()
-        pvlist = lvm.pvlist()
-        for drive in drives:
-            disk = diskset.disks[drive]
-            for part in disk.getLVMPartitions():
-                partname = part.getDeviceNodeName()
-                partrequest = self.getRequestByDeviceName(partname)
-                if partrequest.encryption is None and \
-                   cryptodev.isLuks("/dev/%s" % partname) and \
-                   not self.encryptedDevices.get(partname):
-                    log.debug("ignoring PV %s since we cannot access it's contents" % partname)
-                    # We don't want to treat an encrypted PV like a PV if the
-                    # user chose not to provide a passphrase for this device.
-                    # However, if the LUKS device belongs to a just-deleted
-                    # request then we know it is available.
-                    continue
-                used = 0
-                for volgroup in volgroups:
-                    if volgroup.physicalVolumes:
-                        if partrequest.uniqueID in volgroup.physicalVolumes:
-                            if (request and request.uniqueID and
-                                volgroup.uniqueID == request.uniqueID):
-                                used = 2
-                            else:
-                                used = 1
-
-                    if used:
-                        break
-                size = None
-                for pvpart, pvvg, pvsize in pvlist:
-                    if pvpart == "/dev/%s" % (partname,):
-                        size = pvsize
-                if size is None:
-                    # if we get here, there's no PV data in the partition,
-                    # so clamp the partition's size to 64M
-                    size = part.getSize(unit="MB")
-                    size = lvm.clampPVSize(size, 65536)
-
-                if used == 0:
-                    rc.append((partrequest.uniqueID, size, 0))
-                elif used == 2:
-                    rc.append((partrequest.uniqueID, size, 1))
-
-	# now find available RAID devices
-	raiddev = self.getRaidRequests()
-	if raiddev:
-	    raidcounter = 0
-	    for dev in raiddev:
-                used = 0
-
-		if dev.fstype is None:
-		    continue
-		if dev.fstype.getName() != "physical volume (LVM)":
-		    continue
-		
-                for volgroup in volgroups:
-                    if volgroup.physicalVolumes:
-                        if dev.uniqueID in volgroup.physicalVolumes:
-                            if (request and request.uniqueID and
-                                volgroup.uniqueID == request.uniqueID):
-                                used = 2
-                            else:
-                                used = 1
-
-                    if used:
-                        break
-		    
-                size = dev.getActualSize(self, diskset)
-
-                if used == 0:
-                    rc.append((dev.uniqueID, size, 0))
-                elif used == 2:
-                    rc.append((dev.uniqueID, size, 1))
-
-		raidcounter = raidcounter + 1
-        return rc
-
-    def getLVMVolumeGroupMemberParent(self, request):
-        """Return parent volume group of a physical volume"""
-	volgroups = self.getLVMVGRequests()
-	if not volgroups:
-	    return None
-
-	for volgroup in volgroups:
-	    if volgroup.physicalVolumes:
-		if request.uniqueID in volgroup.physicalVolumes:
-		    return volgroup
-
-	return None
-
-    def isLVMVolumeGroupMember(self, request):
-        """Return whether or not the request is being used in an LVM device."""
-	if self.getLVMVolumeGroupMemberParent(request) is None:
-	    return 0
-	else:
-	    return 1
-    
-    def isVolumeGroupNameInUse(self, vgname):
-        """Return whether or not the requested volume group name is in use."""
-        if not vgname:
-            return None
-
-        lvmrequests = self.getLVMRequests()
-        if lvmrequests:
-            if vgname in lvmrequests.keys():
-                return 1
-
-        lvmrequests = self.getPartialLVMRequests()
-        if lvmrequests:
-            if vgname in lvmrequests:
-                return 1
-
-        return 0
-
-    def getBootableRequest(self):
-        """Return the name of the current 'boot' mount point."""
-        bootreq = None
-
-        if iutil.isEfi():
-            ret = None
-            for req in self.requests:
-                if req.fstype == fsset.fileSystemTypeGet("efi"):
-                    ret = [ req ]
-            if ret:
-                req = self.getRequestByMountPoint("/boot")
-                if req:
-                    ret.append(req)
-            return ret
-        elif iutil.getPPCMachine() == "iSeries":
-            for req in self.requests:
-                if req.fstype == fsset.fileSystemTypeGet("PPC PReP Boot"):
-                    return [ req ]
-            return None
-        elif (iutil.getPPCMachine() == "pSeries"):
-            # pSeries and Mac bootable requests are odd.
-            # have to consider both the PReP or Bootstrap partition (with
-            # potentially > 1 existing) as well as /boot,/
-
-            ret = []
-            for req in self.requests:
-                if req.fstype == fsset.fileSystemTypeGet("PPC PReP Boot"):
-                    ret.append(req)
-
-            # now add the /boot
-            bootreq = self.getRequestByMountPoint("/boot")
-            if not bootreq:
-                bootreq = self.getRequestByMountPoint("/")
-            if bootreq:
-                ret.append(bootreq)
-
-            if len(ret) >= 1:
-                return ret
-            return None
-        elif (iutil.getPPCMachine() == "PMac"):
-            # for the bootstrap partition, we want either the first or the
-            # first non-preexisting one
-            bestprep = None
-            for req in self.requests:
-                if req.fstype == fsset.fileSystemTypeGet("Apple Bootstrap"):
-                    if ((bestprep is None) or
-                        (bestprep.getPreExisting() and
-                         not req.getPreExisting())):
-                        bestprep = req
-
-            if bestprep:
-                ret = [ bestprep ]
-            else:
-                ret = []
-
-            # now add the /boot
-            bootreq = self.getRequestByMountPoint("/boot")
-            if not bootreq:
-                bootreq = self.getRequestByMountPoint("/")
-            if bootreq:
-                ret.append(bootreq)
-
-            if len(ret) >= 1:
-                return ret
-            return None
-        
-        if not bootreq:
-            bootreq = self.getRequestByMountPoint("/boot")
-        if not bootreq:
-            bootreq = self.getRequestByMountPoint("/")
-
-        if bootreq:
-            return [ bootreq ]
-        return None
-
-    def getBootableMountpoints(self):
-        """Return a list of bootable valid mountpoints for this arch."""
-        # FIXME: should be somewhere else, preferably some sort of arch object
-
-        if iutil.isEfi():
-            return [ "/boot/efi" ]
-        else:
-            return [ "/boot", "/" ]
-
-    def isBootable(self, request):
-        """Returns if the request should be considered a 'bootable' request.
-
-        This basically means that it should be sorted to the beginning of
-        the drive to avoid cylinder problems in most cases.
-        """
-        bootreqs = self.getBootableRequest()
-        if not bootreqs:
-            return 0
-
-        for bootreq in bootreqs:
-            if bootreq == request:
-                return 1
-
-            if bootreq.type == REQUEST_RAID and \
-                   request.uniqueID in bootreq.raidmembers:
-                return 1
-
-        return 0
-
-    def sortRequests(self):
-        """Resort the requests into allocation order."""
-        n = 0
-        while n < len(self.requests):
-	    # Ignore LVM Volume Group and Logical Volume requests,
-	    # since these are not related to allocating disk partitions
-	    if (self.requests[n].type == REQUEST_VG or self.requests[n].type == REQUEST_LV):
-		n = n + 1
-		continue
-	    
-            for request in self.requests:
-		# Ignore LVM Volume Group and Logical Volume requests,
-		# since these are not related to allocating disk partitions
-		if (request.type == REQUEST_VG or request.type == REQUEST_LV):
-		    continue
-                # for raid requests, the only thing that matters for sorting
-                # is the raid device since ordering by size is mostly
-                # irrelevant.  this also keeps things more consistent
-                elif (request.type == REQUEST_RAID or
-                    self.requests[n].type == REQUEST_RAID):
-                    if (request.type == self.requests[n].type and
-                        (self.requests[n].raidminor != None) and
-                        ((request.raidminor is None) or
-                         request.raidminor > self.requests[n].raidminor)):
-                        tmp = self.requests[n]
-                        index = self.requests.index(request)
-                        self.requests[n] = request
-                        self.requests[index] = tmp
-                # for sized requests, we want the larger ones first
-                elif (request.size and self.requests[n].size and
-                    (request.size < self.requests[n].size)):
-                    tmp = self.requests[n]
-                    index = self.requests.index(request)
-                    self.requests[n] = request
-                    self.requests[index] = tmp
-                # for cylinder-based, sort by order on the drive
-                elif (request.start and self.requests[n].start and
-                      (request.drive == self.requests[n].drive) and
-                      (request.type == self.requests[n].type) and 
-                      (request.start > self.requests[n].start)):
-                    tmp = self.requests[n]
-                    index = self.requests.index(request)
-                    self.requests[n] = request
-                    self.requests[index] = tmp
-                # finally just use when they defined the partition so
-                # there's no randomness thrown in
-                elif (request.size and self.requests[n].size and
-                      (request.size == self.requests[n].size) and
-                      (request.uniqueID < self.requests[n].uniqueID)):
-                    tmp = self.requests[n]
-                    index = self.requests.index(request)
-                    self.requests[n] = request
-                    self.requests[index] = tmp                
-            n = n + 1
-
-        tmp = self.getBootableRequest()
-
-        boot = []
-        if tmp:
-            for req in tmp:
-                # if raid, we want all of the contents of the bootable raid
-                if req.type == REQUEST_RAID:
-                    for member in req.raidmembers:
-                        boot.append(self.getRequestByID(member))
-                else:
-                    boot.append(req)
-
-        # remove the bootables from the request
-        for bootable in boot:
-            self.requests.pop(self.requests.index(bootable))
-
-        # move to the front of the list
-        boot.extend(self.requests)
-        self.requests = boot
-
-    def sanityCheckAllRequests(self, diskset, baseChecks = 0):
-        """Do a sanity check of all of the requests.
-
-        This function is called at the end of partitioning so that we
-        can make sure you don't have anything silly (like no /, a really
-        small /, etc).  Returns (errors, warnings) where each is a list
-        of strings or None if there are none.
-        If baseChecks is set, the basic sanity tests which the UI runs prior to
-        accepting a partition will be run on the requests as well.
-        """
-        checkSizes = [('/usr', 250), ('/tmp', 50), ('/var', 384),
-                      ('/home', 100), ('/boot', 75)]
-        warnings = []
-        errors = []
-
-        slash = self.getRequestByMountPoint('/')
-        if not slash:
-            errors.append(_("You have not defined a root partition (/), "
-                            "which is required for installation of %s "
-                            "to continue.") % (productName,))
-
-        if slash and slash.getActualSize(self, diskset) < 250:
-            warnings.append(_("Your root partition is less than 250 "
-                              "megabytes which is usually too small to "
-                              "install %s.") % (productName,))
-
-        if (slash and self.anaconda and 
-            (slash.getActualSize(self, diskset) < 
-             self.anaconda.backend.getMinimumSizeMB("/"))):
-            errors.append(_("Your %s partition is less than %s "
-                            "megabytes which is lower than recommended "
-                            "for a normal %s install.")
-                          %("/", self.anaconda.backend.getMinimumSizeMB("/"), 
-                            productName))
-
-        def getBaseReqs(reqs):
-            n = 0
-            while not reduce(lambda x,y: x and (y.type not in [REQUEST_RAID, REQUEST_LV]),
-                             reqs, True) \
-                    and len(reqs) > n:
-                req = reqs[n]
-                if req.type == REQUEST_RAID:
-                    for id in req.raidmembers:
-                        reqs.append(self.getRequestByID(id))
-                    del reqs[n]
-                    continue
-                elif req.type == REQUEST_LV:
-                    del reqs[n]
-                    continue
-                n += 1
-            return reqs
-
-        if iutil.isEfi():
-            bootreq = self.getRequestByMountPoint("/boot/efi")
-            if (not bootreq) or \
-                    bootreq.fstype != fsset.fileSystemTypeGet("efi") or \
-                    bootreq.getActualSize(self, diskset) < 10:
-                errors.append(_("You must create an EFI System Partition of "
-                                "at least 10 megabytes."))
-        elif iutil.isX86():
-            if iutil.isMactel():
-                # mactel checks
-                bootreqs = self.getBootableRequest() or []
-                for br in getBaseReqs(bootreqs):
-                    if not isinstance(br, partRequests.PartitionSpec):
-                        continue
-                    (dev, num) = fsset.getDiskPart(br.device)
-
-                    if partedUtils.hasGptLabel(diskset, dev) \
-                            and int(num) > 4:
-                        errors.append(
-                            _("Your boot partition isn't on one of "
-                              "the first four partitions and thus "
-                              "won't be bootable."))
-
-        if iutil.getPPCMacGen() == "NewWorld":
-            reqs = self.getBootableRequest()
-            found = 0
-
-            bestreq = None
-            if reqs:
-                for req in reqs:
-                    if req.fstype == fsset.fileSystemTypeGet("Apple Bootstrap"):
-                        found = 1
-                        # the best one is either the first or the first
-                        # newly formatted one
-                        if ((bestreq is None) or ((bestreq.format == 0) and
-                                                  (req.format == 1))):
-                            bestreq = req
-                        break
-                
-            if not found:
-                errors.append(_("You must create an Apple Bootstrap partition."))
-
-        if (iutil.getPPCMachine() == "pSeries" or
-            iutil.getPPCMachine() == "iSeries"):
-            reqs = self.getBootableRequest()
-            found = 0
-
-            bestreq = None
-            if reqs:
-                for req in reqs:
-                    if req.fstype == fsset.fileSystemTypeGet("PPC PReP Boot"):
-                        found = 1
-                        # the best one is either the first or the first
-                        # newly formatted one
-                        if ((bestreq is None) or ((bestreq.format == 0) and
-                                                  (req.format == 1))):
-                            bestreq = req
-                        break
-            if iutil.getPPCMachine() == "iSeries" and iutil.hasiSeriesNativeStorage():
-                found = 1
-                
-            if not found:
-                errors.append(_("You must create a PPC PReP Boot partition."))
-
-            if bestreq is not None:
-                if (iutil.getPPCMachine() == "pSeries"):
-                    minsize = 2
-                else:
-                    minsize = 12
-                if bestreq.getActualSize(self, diskset) < minsize:
-                    warnings.append(_("Your %s partition is less than %s "
-                                      "megabytes which is lower than "
-                                      "recommended for a normal %s install.")
-                                    %(_("PPC PReP Boot"), minsize, productName))
-                    
-
-        for (mount, size) in checkSizes:
-            req = self.getRequestByMountPoint(mount)
-            if not req:
-                continue
-            if req.getActualSize(self, diskset) < size:
-                warnings.append(_("Your %s partition is less than %s "
-                                  "megabytes which is lower than recommended "
-                                  "for a normal %s install.")
-                                %(mount, size, productName))
-
-        foundSwap = 0
-        swapSize = 0
-        usesUSB = False
-        usesFireWire = False
-
-        for request in self.requests:
-            if request.fstype and request.fstype.getName() == "swap":
-                foundSwap = foundSwap + 1
-                swapSize = swapSize + request.getActualSize(self, diskset)
-            if baseChecks:
-                rc = request.doSizeSanityCheck()
-                if rc:
-                    warnings.append(rc)
-                rc = request.doMountPointLinuxFSChecks()
-                if rc:
-                    errors.append(rc)
-                if isinstance(request, partRequests.RaidRequestSpec):
-                    rc = request.sanityCheckRaid(self)
-                    if rc:
-                        errors.append(rc)
-            if not hasattr(request,'drive'):
-                continue
-            for x in request.drive or []:
-                if isys.driveUsesModule(x, ["usb-storage", "ub"]):
-                    usesUSB = True
-                elif isys.driveUsesModule(x, ["sbp2", "firewire-sbp2"]):
-                    usesFireWire = True
-            
-        if usesUSB:
-            warnings.append(_("Installing on a USB device.  This may "
-                              "or may not produce a working system."))
-        if usesFireWire:
-            warnings.append(_("Installing on a FireWire device.  This may "
-                              "or may not produce a working system."))
-
-        bootreqs = self.getBootableRequest()
-        if bootreqs:
-            for bootreq in bootreqs:
-                if not bootreq:
-                    continue
-                if (isinstance(bootreq, partRequests.RaidRequestSpec) and
-                    not raid.isRaid1(bootreq.raidlevel)):
-                    errors.append(_("Bootable partitions can only be on RAID1 "
-                                    "devices."))
-
-                # can't have bootable partition on LV
-                if (isinstance(bootreq, partRequests.LogicalVolumeRequestSpec)):
-                    errors.append(_("Bootable partitions cannot be on a "
-                                    "logical volume."))
-
-                # most arches can't have boot on RAID
-                if (isinstance(bootreq, partRequests.RaidRequestSpec) and
-                    iutil.getArch() not in raid.raidBootArches):
-                    errors.append(_("Bootable partitions cannot be on a RAID "
-                                    "device."))
-
-                # Lots of filesystems types don't support /boot.
-                if (bootreq.fstype and not bootreq.fstype.isBootable()):
-                    errors.append(_("Bootable partitions cannot be on an %s "
-                                    "filesystem.")%(bootreq.fstype.getName(),))
-
-                # vfat /boot is insane.
-                if (bootreq.mountpoint and bootreq.mountpoint == "/" and
-                    bootreq.fstype and bootreq.fstype.getName() == "vfat"):
-                    errors.append(_("Bootable partitions cannot be on an %s "
-                                    "filesystem.")%(bootreq.fstype.getName(),))
-
-                if (bootreq.isEncrypted(self)):
-                    errors.append(_("Bootable partitions cannot be on an "
-                                    "encrypted block device"))
-
-        if foundSwap == 0:
-            warnings.append(_("You have not specified a swap partition.  "
-                              "Although not strictly required in all cases, "
-                              "it will significantly improve performance for "
-                              "most installations."))
-
-        # XXX number of swaps not exported from kernel and could change
-        if foundSwap >= 32:
-            warnings.append(_("You have specified more than 32 swap devices.  "
-                              "The kernel for %s only supports 32 "
-                              "swap devices.") % (productName,))
-
-        mem = iutil.memInstalled()
-        rem = mem % 16384
-        if rem:
-            mem = mem + (16384 - rem)
-        mem = mem / 1024
-
-        if foundSwap and (swapSize < (mem - 8)) and (mem < 1024):
-            warnings.append(_("You have allocated less swap space (%dM) than "
-                              "available RAM (%dM) on your system.  This "
-                              "could negatively impact performance.")
-                            %(swapSize, mem))
-
-        if warnings == []:
-            warnings = None
-        if errors == []:
-            errors = None
-
-        return (errors, warnings)
-
-    def setProtected(self, dispatch):
-        """Set any partitions which should be protected to be so."""
-        for device in self.protectedPartitions():
-            log.info("%s is a protected partition" % (device))
-            request = self.getRequestByDeviceName(device)
-            if request is not None:
-                request.setProtected(1)
-            else:
-                log.info("no request, probably a removable drive")
-
-    def copy (self):
-        """Deep copy the object."""
-        new = Partitions(self.anaconda)
-        for request in self.requests:
-            new.addRequest(request)
-        for delete in self.deletes:
-            new.addDelete(delete)
-        new.autoPartitionRequests = self.autoPartitionRequests
-        new.autoClearPartType = self.autoClearPartType
-        new.autoClearPartDrives = self.autoClearPartDrives
-        new.nextUniqueID = self.nextUniqueID
-        new.useAutopartitioning = self.useAutopartitioning
-        new.useFdisk = self.useFdisk
-        new.reinitializeDisks = self.reinitializeDisks
-        new.protected = self.protected
-        return new
-
-    def getClearPart(self):
-        """Get the kickstart directive related to the clearpart being used."""
-        clearpartargs = []
-        if self.autoClearPartType == CLEARPART_TYPE_LINUX:
-            clearpartargs.append('--linux')
-        elif self.autoClearPartType == CLEARPART_TYPE_ALL:
-            clearpartargs.append('--all')
-        else:
-            return None
-
-        if self.reinitializeDisks:
-            clearpartargs.append('--initlabel')
-
-        if self.autoClearPartDrives:
-            drives = string.join(self.autoClearPartDrives, ',')
-            clearpartargs.append('--drives=%s' % (drives))
-
-        return "#clearpart %s\n" %(string.join(clearpartargs))
-    
-    def writeKS(self, f):
-        """Write out the partitioning information in kickstart format."""
-        f.write("# The following is the partition information you requested\n")
-        f.write("# Note that any partitions you deleted are not expressed\n")
-        f.write("# here so unless you clear all partitions first, this is\n")
-        f.write("# not guaranteed to work\n")
-        clearpart = self.getClearPart()
-        if clearpart:
-            f.write(clearpart)
-
-        # lots of passes here -- parts, raid, volgroup, logvol
-        # XXX what do we do with deleted partitions?
-        for request in self.requests:
-            args = []
-            if request.type == REQUEST_RAID:
-                continue
-
-            # no fstype, no deal (same with foreigns)
-            if not request.fstype or request.fstype.getName() == "foreign":
-                continue
-
-            # first argument is mountpoint, which can also be swap or
-            # the unique RAID identifier.  I hate kickstart partitioning
-            # syntax.  a lot.  too many special cases 
-            if request.fstype.getName() == "swap":
-                args.append("swap")
-            elif request.fstype.getName() == "software RAID":
-                # since we guarantee that uniqueIDs are ints now...
-                args.append("raid.%s" % (request.uniqueID))
-            elif request.fstype.getName() == "physical volume (LVM)":
-                # see above about uniqueIDs being ints
-                args.append("pv.%s" % (request.uniqueID))
-            elif request.fstype.getName() == "PPC PReP Boot":
-                args.extend(["prepboot", "--fstype", "\"PPC PReP Boot\""])
-            elif request.fstype.getName() == "Apple Bootstrap":
-                args.extend(["appleboot", "--fstype", "\"Apple Bootstrap\""])
-            elif request.mountpoint:
-                args.extend([request.mountpoint, "--fstype",
-                             request.fstype.getName(quoted = 1)])
-            else:
-                continue
-
-            # generic options
-            if not request.format:
-                args.append("--noformat")
-
-            # device encryption
-            if request.encryption:
-                args.append("--encrypted")
-
-            # preexisting only
-            if request.type == REQUEST_PREEXIST and request.device:
-                args.append("--onpart")
-                args.append(request.device)
-            # we have a billion ways to specify new partitions
-            elif request.type == REQUEST_NEW:
-                if request.size:
-                    args.append("--size=%s" % (int(request.size),))
-                if request.size == 0:
-                    args.append("--size=0")
-                if request.grow:
-                    args.append("--grow")
-                if request.start:
-                    args.append("--start=%s" % (request.start))
-                if request.end:
-                    args.append("--end=%s" % (request.end))
-                if request.maxSizeMB:
-                    args.append("--maxsize=%s" % (request.maxSizeMB))
-                if request.drive:
-                    args.append("--ondisk=%s" % (request.drive[0]))
-                if request.primary:
-                    args.append("--asprimary")
-            else: # how the hell did we get this?
-                continue
-
-            f.write("#part %s\n" % (string.join(args)))
-
-
-        for request in self.requests:
-            args = []
-            if request.type != REQUEST_RAID:
-                continue
-
-            # no fstype, no deal (same with foreigns)
-            if not request.fstype or request.fstype.getName() == "foreign":
-                continue
-
-            # also require a raidlevel and raidmembers for raid
-            if (request.raidlevel == None) or not request.raidmembers:
-                continue
-
-            # first argument is mountpoint, which can also be swap
-            if request.fstype.getName() == "swap":
-                args.append("swap")
-            elif request.fstype.getName() == "physical volume (LVM)":
-                # see above about uniqueIDs being ints
-                args.append("pv.%s" % (request.uniqueID))
-            elif request.mountpoint:
-                args.append(request.mountpoint)
-            else:
-                continue
-
-            # generic options
-            if not request.format:
-                args.append("--noformat")
-            if request.preexist:
-                args.append("--useexisting")
-            if request.fstype:
-                args.extend(["--fstype", request.fstype.getName(quoted = 1)])
-
-            # device encryption
-            if request.encryption:
-                args.append("--encrypted")
-
-            args.append("--level=%s" % (request.raidlevel))
-            args.append("--device=md%s" % (request.raidminor))
-
-            if request.raidspares:
-                args.append("--spares=%s" % (request.raidspares))
-
-            # silly raid member syntax
-            raidmems = []
-            for member in request.raidmembers:
-                if (type(member) != type("")) or (member[0:5] != "raid."):
-                    raidmems.append("raid.%s" % (member))
-                else:
-                    raidmems.append(member)
-            args.append("%s" % (string.join(raidmems)))
-
-            f.write("#raid %s\n" % (string.join(args)))
-
-        for request in self.requests:
-            args = []
-            if request.type != REQUEST_VG:
-                continue
-
-            args.append(request.volumeGroupName)
-
-            # generic options
-            if not request.format:
-                args.append("--noformat")
-            if request.preexist:
-                args.append("--useexisting")
-
-            args.append("--pesize=%s" %(request.pesize,))
-
-            # silly pv syntax
-            pvs = []
-            for member in request.physicalVolumes:
-                if (type(member) != type("")) or not member.startswith("pv."):
-                    pvs.append("pv.%s" % (member))
-                else:
-                    pvs.append(member)
-            args.append("%s" % (string.join(pvs)))
-
-            f.write("#volgroup %s\n" % (string.join(args)))
-
-        for request in self.requests:
-            args = []
-            if request.type != REQUEST_LV:
-                continue
-
-            # no fstype, no deal (same with foreigns)
-            if not request.fstype or request.fstype.getName() == "foreign":
-                continue
-
-            # require a vg name and an lv name
-            if (request.logicalVolumeName is None or
-                request.volumeGroup is None):
-                continue
-
-            # first argument is mountpoint, which can also be swap
-            if request.fstype.getName() == "swap":
-                args.append("swap")
-            elif request.mountpoint:
-                args.append(request.mountpoint)
-            else:
-                continue
-
-            # generic options
-            if not request.format:
-                args.append("--noformat")
-            if request.preexist:
-                args.append("--useexisting")
-            if request.fstype:
-                args.extend(["--fstype", request.fstype.getName(quoted = 1)])
-
-            # device encryption
-            if request.encryption:
-                args.append("--encrypted")
-
-            vg = self.getRequestByID(request.volumeGroup)
-            if vg is None:
-                continue
-
-            args.extend(["--name=%s" %(request.logicalVolumeName,),
-                         "--vgname=%s" %(vg.volumeGroupName,)])
-
-	    if request.grow:
-		if request.startSize:
-		    args.append("--size=%s" % (int(request.startSize),))
-		else:
-		    # shouldnt happen
-		    continue
-		
-		args.append("--grow")
-		if request.maxSizeMB and int(request.maxSizeMB) > 0:
-		    args.append("--maxsize=%s" % (request.maxSizeMB,))
-	    else:
-		if request.percent:
-		    args.append("--percent=%s" %(request.percent,))
-		elif request.size:
-		    args.append("--size=%s" %(int(request.size),))
-		else:
-		    continue
-
-            f.write("#logvol %s\n" % (string.join(args)))            
-
-    def deleteAllLogicalPartitions(self, part):
-        """Add delete specs for all logical partitions in part."""
-        for partition in part.disk.getLogicalPartitions():
-            partName = partition.getDeviceNodeName()
-            request = self.getRequestByDeviceName(partName)
-            self.removeRequest(request)
-            if request.preexist:
-                drive = partedUtils.get_partition_drive(partition)
-                delete = partRequests.DeleteSpec(drive,
-                                                 partition.geometry.start,
-                                                 partition.geometry.end)
-                self.addDelete(delete)
-
-    def containsImmutablePart(self, part):
-        """Returns whether the partition contains parts we can't delete."""
-        if not part or (type(part) == type("RAID")) or (type(part) == type(1)):
-            return None
-
-        if not part.type & parted.PARTITION_EXTENDED:
-            return None
-
-        disk = part.disk
-        while part:
-            if not part.active:
-                part = disk.nextPartition(part)
-                continue
-
-            device = part.getDeviceNodeName()
-            request = self.getRequestByDeviceName(device)
-
-            if request:
-                if request.getProtected():
-                    return _("the partition in use by the installer.")
-
-                if self.isRaidMember(request):
-                    return _("a partition which is a member of a RAID array.")
-
-                if self.isLVMVolumeGroupMember(request):
-                    return _("a partition which is a member of a LVM Volume Group.")
-
-            part = disk.nextPartition(part)
-        return None
-
-
-    def doMetaDeletes(self, diskset):
-        """Does the removal of all of the non-physical volumes in the delete list."""
-
-        # have to have lvm on, which requires raid to be started
-        diskset.startMPath()
-        diskset.startDmRaid()
-        diskset.startMdRaid()
-        for luksDev in self.encryptedDevices.values():
-            luksDev.openDevice()
-        lvm.vgactivate()
-
-        snapshots = {}
-        for (lvvg, lv, size, lvorigin) in lvm.lvlist():
-            snapshots.setdefault(lv, [])
-            if lvorigin:
-                snapshots.setdefault(lvorigin, [])
-                snapshots[lvorigin].append((lv, lvvg))
-
-        lvm_parent_deletes = []
-        tmp = {}
-        def addSnap(name, vg):
-            if not snapshots.has_key(name):
-                return
-            snaps = snapshots[name]
-            for snap, snapvg in snaps:
-                addSnap(snap, snapvg)
-            if not tmp.has_key((name, vg)):
-                tmp[(name, vg)] = 1
-                lvm_parent_deletes.append((name,vg))
-
-        # now, go through and delete logical volumes
-        for delete in self.deletes:
-            if isinstance(delete, partRequests.DeleteLogicalVolumeSpec):
-                if not delete.beenDeleted():
-                    addSnap(delete.name, delete.vg)
-                    delete.setDeleted(1)
-
-        for name,vg in lvm_parent_deletes:
-            log.info("removing lv %s" % (name,))
-            key = "mapper/%s-%s" % (vg, name)
-            if key in self.encryptedDevices.keys():
-                self.encryptedDevices[key].closeDevice()
-                del self.encryptedDevices[key]
-            lvm.lvremove(name, vg)
-
-        # now, go through and delete volume groups
-        for delete in self.deletes:
-            if isinstance(delete, partRequests.DeleteVolumeGroupSpec):
-                if not delete.beenDeleted():
-                    lvm.vgremove(delete.name)
-                    delete.setDeleted(1)
-
-        lvm.vgdeactivate()
-
-        # now, remove obsolete cryptodev instances
-        for (device, luksDev) in self.encryptedDevices.items():
-            luksDev.closeDevice()
-            found = 0
-            for req in self.requests:
-                if req.encryption == luksDev:
-                    found = 1
-
-            if not found:
-                del self.encryptedDevices[device]
-
-        diskset.stopMdRaid()
-
-    def doMetaResizes(self, diskset):
-        """Does resizing of non-physical volumes."""
-
-        # have to have lvm on, which requires raid to be started
-        devicesActive = diskset.devicesOpen
-        if not devicesActive:
-            # should this not be diskset.openDevices() ?
-            diskset.startMPath()
-            diskset.startDmRaid()
-            diskset.startMdRaid()
-
-        for luksDev in self.encryptedDevices.values():
-            luksDev.openDevice()
-        lvm.vgactivate()
-
-        # we only support resizing LVM of these types of things currently
-        for lv in self.getLVMLVRequests():
-            if not lv.preexist:
-                continue
-            if lv.targetSize is None:
-                continue
-
-            vg = self.getRequestByID(lv.volumeGroup)
-            if vg is None:
-                continue
-
-            lvm.lvresize(lv.logicalVolumeName, vg.volumeGroupName,
-                         lv.targetSize)
-
-        lvm.vgdeactivate()
-        for luksDev in self.encryptedDevices.values():
-            luksDev.closeDevice()
-
-        if not devicesActive:
-            # should this not be diskset.closeDevices() ?
-            diskset.stopMdRaid()
-            diskset.stopDmRaid()
-            diskset.stopMPath()
-
-    def doEncryptionRetrofits(self):
-        if not self.retrofitPassphrase or not self.encryptionPassphrase:
-            return
-
-        for request in self.requests:
-            if not request.encryption:
-                continue
-
-            # XXX this will only work before the new LUKS devices are created
-            #     since the format flag gets unset when they are formatted
-            if request.encryption.format:
-                continue
-
-            if request.encryption.addPassphrase(self.encryptionPassphrase):
-                log.error("failed to add new passphrase to existing device %s" % (request.encryption.getDevice(encrypted=1),))
-
-    def deleteDependentRequests(self, request):
-        """Handle deletion of this request and all requests which depend on it.
-
-        eg, delete all logical volumes from a volume group, all volume groups
-        which depend on the raid device.
-
-        Side effects: removes all dependent requests from self.requests
-                      adds needed dependent deletes to self.deletes
-        """
-
-        toRemove = []
-        id = request.uniqueID
-        for req in self.requests:
-            if isinstance(req, partRequests.RaidRequestSpec):
-                if id in req.raidmembers:
-                    toRemove.append(req)
-                # XXX do we need to do anything special with preexisting raids?
-            elif isinstance(req, partRequests.VolumeGroupRequestSpec):
-                if id in req.physicalVolumes:
-                    toRemove.append(req)
-                    if req.getPreExisting():
-                        delete = partRequests.DeleteVolumeGroupSpec(req.volumeGroupName)
-                        self.addDelete(delete)
-            elif isinstance(req, partRequests.LogicalVolumeRequestSpec):
-                if id == req.volumeGroup:
-                    toRemove.append(req)
-                    tmp = self.getRequestByID(req.volumeGroup)
-                    if not tmp:
-                        log.error("Unable to find the vg for %s"
-                                  % (req.logicalVolumeName,))
-                        vgname = req.volumeGroup
-                    else:
-                        vgname = tmp.volumeGroupName
-
-                    if req.getPreExisting():
-                        delete = partRequests.DeleteLogicalVolumeSpec(req.logicalVolumeName,
-                                                                      vgname)
-                        self.addDelete(delete)
-
-        for req in toRemove:
-            self.deleteDependentRequests(req)
-            self.removeRequest(req)
-        
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/partRequests.py anaconda-storage/partRequests.py
--- anaconda/partRequests.py	2009-02-13 23:03:10.000000000 -0600
+++ anaconda-storage/partRequests.py	1969-12-31 18:00:00.000000000 -0600
@@ -1,1053 +0,0 @@
-#
-# partRequests.py: partition request objects and management thereof
-#
-# Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007  Red Hat, Inc.
-# All rights reserved.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-#
-# Author(s): Matt Wilson <msw@redhat.com>
-#            Jeremy Katz <katzj@redhat.com>
-#            Mike Fulbright <msf@redhat.com>
-#            Harald Hoyer <harald@redhat.de>
-#
-
-"""Partition request objects and management thereof."""
-
-import parted
-import iutil
-import string
-import os, sys, math
-
-from constants import *
-from flags import *
-
-import gettext
-_ = lambda x: gettext.ldgettext("anaconda", x)
-
-import fsset
-import raid
-import lvm
-import partedUtils
-import partIntfHelpers
-
-import logging
-log = logging.getLogger("anaconda")
-
-class DeleteSpec:
-    """Defines a preexisting partition which is intended to be removed."""
-    
-    def __init__(self, drive, start, end):
-        """Initializes a DeleteSpec.
-
-        drive is the text form of the drive
-        start is the start sector of the deleted partition
-        end is the end sector of the deleted partition
-        """
-        
-        self.drive = drive
-        self.start = start
-        self.end = end
-
-    def __str__(self):
-        return "drive: %s  start: %s  end: %s" %(self.drive, self.start,
-                                                 self.end)
-
-class DeleteLogicalVolumeSpec:
-    """Defines a preexisting logical volume which is intended to be removed."""
-
-    def __init__(self, name, vg):
-        """Initializes a DeleteLogicalVolumeSpec.
-
-        name is the name of the lv
-        vg is the name of the volume group
-        """
-
-        self.name = name
-        self.vg = vg
-        self.deleted = 0
-
-    def __str__(self):
-        return "lvname: %s  vgname: %s" %(self.name, self.vg)
-
-    def beenDeleted(self):
-        return self.deleted
-
-    def setDeleted(self, val):
-        self.deleted = val
-
-class DeleteVolumeGroupSpec:
-    """Defines a preexisting volume group which is intended to be removed."""
-
-    def __init__(self, name):
-        """Initializes a DeleteVolumeGroupSpec
-
-        name is the name of the volume group
-        """
-
-        self.name = name
-        self.deleted = 0
-
-    def __str__(self):
-        return "vgname: %s" %(self.name,)
-
-    def beenDeleted(self):
-        return self.deleted
-
-    def setDeleted(self, val):
-        self.deleted = val
-
-class DeleteRAIDSpec:
-    """Defines a preexisting RAID device which is intended to be removed."""
-
-    def __init__(self, minor):
-        """Initializes a DeleteRAIDSpec.
-
-        minor is the minor of the RAID device being removed
-        """
-
-        self.minor = minor
-
-    def __str__(self):
-        return "minor: %s" %(self.minor,)
-
-class RequestSpec:
-    """Generic Request specification."""
-    def __init__(self, fstype, size = None, mountpoint = None, format = None,
-                 preexist = 0, fslabel = None,
-                 migrate = None, origfstype = None,
-                 fsprofile = None):
-        """Create a generic RequestSpec.
-
-        This should probably never be externally used.
-        """
-
-        self.fstype = fstype
-        self.mountpoint = mountpoint
-        self.size = size
-        self.format = format
-
-        self.migrate = migrate
-        self.origfstype = origfstype
-        self.fslabel = fslabel
-        self.fsopts = None
-        self.fsprofile = fsprofile
-
-        self.device = None
-        """what we currently think the device is"""
-
-        self.uniqueID = None
-        """uniqueID is an integer and *MUST* be unique."""
-
-        self.ignoreBootConstraints = 0
-        """Booting constraints should be ignored for this request."""
-
-        self.preexist = preexist
-        """Did this partition exist before we started playing with things?"""
-
-        self.protected = 0
-        """Is this partitiion 'protected', ie does it contain install media."""
-
-        self.dev = None
-        """A Device() as defined in fsset.py to correspond to this request."""
-
-        self.encryption = None
-        """An optional LUKSDevice() describing block device encryption."""
-
-        self.targetSize = None
-        """Size to resize to"""
-
-        self.resizable = False
-        """Is this a request that can be resized?"""
-
-    def __str__(self):
-        if self.fstype:
-            fsname = self.fstype.getName()
-        else:
-            fsname = "None"
-
-        str = ("Generic Request -- mountpoint: %(mount)s  uniqueID: %(id)s\n"
-               "  type: %(fstype)s  format: %(format)s\n"
-               "  device: %(dev)s  migrate: %(migrate)s  fslabel: %(fslabel)s\n"
-               "  options: '%(fsopts)s'"
-               "  fsprofile: %(fsprofile)s" % 
-               {"mount": self.mountpoint, "id": self.uniqueID,
-                "fstype": fsname, "format": self.format,
-                "dev": self.device, "migrate": self.migrate,
-                "fslabel": self.fslabel,
-                "fsopts": self.fsopts, "fsprofile": self.fsprofile})
-        return str
-
-    def getActualSize(self, partitions, diskset):
-        """Return the actual size allocated for the request in megabytes."""
-
-        sys.stderr.write("WARNING: Abstract RequestSpec.getActualSize() called\n")
-        import traceback
-        traceback.print_stack()
-
-    def getDevice(self, partitions):
-        """Return a device to solidify."""
-
-        sys.stderr.write("WARNING: Abstract RequestSpec.getDevice() called\n")
-        import traceback
-        traceback.print_stack()
-
-    def isResizable(self, partitions):
-        if self.isEncrypted(partitions): # FIXME: can't resize crypted devs yet
-            return False
-        return self.resizable and self.fstype is not None and self.fstype.isResizable()
-
-    def isEncrypted(self, partitions, parentOnly = False):
-        if self.encryption:
-            return True
-        return False
-
-    def toEntry(self, partitions):
-        """Turn a request into a fsset entry and return the entry."""
-        device = self.getDevice(partitions)
-
-        # pin down our partitions so that we can reread the table
-        device.solidify()
-
-        if self.fstype.getName() == "swap":
-            mountpoint = "swap"
-        else:
-            mountpoint = self.mountpoint
-
-        entry = fsset.FileSystemSetEntry(device, mountpoint, self.fstype,
-                                         origfsystem=self.origfstype,
-                                         options=self.fsopts,
-                                         fsprofile=self.fsprofile)
-        if self.format:
-            entry.setFormat(self.format)
-
-        if self.migrate:
-            entry.setMigrate(self.migrate)
-
-        if self.fslabel:
-            entry.setLabel(self.fslabel)
-
-        if self.targetSize and self.fstype.isResizable():
-            entry.setResizeTarget(self.targetSize, self.size)
-
-        return entry
-
-    def setProtected(self, val):
-        """Set the protected value for this partition."""
-        self.protected = val
-
-    def getProtected(self):
-        """Return the protected value for this partition."""
-        return self.protected
-
-    def getPreExisting(self):
-        """Return whether the partition existed before we started playing."""
-        return self.preexist
-
-    def doMountPointLinuxFSChecks(self):
-        """Return an error string if the mountpoint is not valid for Linux FS."""
-        mustbeonroot = ('/bin','/dev','/sbin','/etc','/lib','/root',
-                        '/mnt', 'lost+found', '/proc')
-        mustbeonlinuxfs = ('/', '/boot', '/var', '/tmp', '/usr', '/home',
-                           '/usr/share', '/usr/lib' )
-
-	# these are symlinks so you cant make them mount points
-	otherexcept = ('/var/mail', '/usr/bin/X11', '/usr/lib/X11', '/usr/tmp')
-
-        if not self.mountpoint:
-            return None
-
-        if self.fstype is None:
-            return None
-
-        if flags.livecdInstall and self.mountpoint == "/" and not self.format:
-            return _("The mount point %s must be formatted during live CD "
-                     "installs.") % self.mountpoint
-        if flags.livecdInstall and self.mountpoint == "/" and self.fstype.getName() not in ["ext4", "ext3", "ext2"]:
-            return _("The mount point %s must be formatted to match the live "
-                     "rootfs during live CD installs.") % self.mountpoint
-
-        if self.fstype.isMountable():
-            if self.mountpoint in mustbeonroot:
-                return _("This mount point is invalid.  The %s directory must "
-                         "be on the / file system.") % (self.mountpoint,)
-	    elif self.mountpoint in otherexcept:
-                return _("The mount point %s cannot be used.  It must "
-			 "be a symbolic link for proper system "
-			 "operation.  Please select a different "
-			 "mount point.") % (self.mountpoint,)
-
-            return None
-
-        if not self.fstype.isLinuxNativeFS():
-            if self.mountpoint in mustbeonlinuxfs:
-                return _("This mount point must be on a linux file system.")
-
-        return None
-
-    # requestSkipList is a list of uids for requests to ignore when
-    # looking for a conflict on the mount point name.  Used in lvm
-    # editting code in disk druid, for example.
-    def isMountPointInUse(self, partitions, requestSkipList=None):
-        """Return whether my mountpoint is in use by another request."""
-        mntpt = self.mountpoint
-        if not mntpt:
-            return None
-
-        if partitions and partitions.requests:
-            for request in partitions.requests:
-		if requestSkipList is not None and request.uniqueID in requestSkipList:
-		    continue
-
-                if request.mountpoint == mntpt:
-                    if (not self.uniqueID or
-                        request.uniqueID != self.uniqueID):
-                        return _("The mount point \"%s\" is already in use, "
-                                 "please choose a different mount point."
-                                 %(mntpt))
-        return None
-
-    def doSizeSanityCheck(self):
-        """Sanity check that the size of the request is sane."""
-        if not self.fstype:
-            return None
-
-        if not self.format:
-            return None
-
-        if self.size and self.size > self.fstype.getMaxSizeMB():
-            return (_("The size of the %s partition (%10.2f MB) "
-                      "exceeds the maximum size of %10.2f MB.")
-                    % (self.fstype.getName(), self.size,
-                       self.fstype.getMaxSizeMB()))
-        
-        return None
-
-    # set skipMntPtExistCheck to non-zero if you want to handle this
-    # check yourself. Used in lvm volume group editting code, for example.
-    def sanityCheckRequest(self, partitions, skipMntPtExistCheck=0):
-        """Run the basic sanity checks on the request."""
-        # see if mount point is valid if its a new partition request
-        mntpt = self.mountpoint
-        fstype = self.fstype
-        preexist = self.preexist
-        format = self.format
-
-        rc = self.doSizeSanityCheck()
-        if rc:
-            return rc
-
-        rc = partIntfHelpers.sanityCheckMountPoint(mntpt, fstype, preexist, format)
-        if rc:
-            return rc
-
-	if not skipMntPtExistCheck:
-	    rc = self.isMountPointInUse(partitions)
-	    if rc:
-		return rc
-
-        rc = self.doMountPointLinuxFSChecks()
-        if rc:
-            return rc
-
-        return None
-        
-
-    def formatByDefault(self):
-        """Return whether or not the request should be formatted by default."""
-        def inExceptionList(mntpt):
-            exceptlist = ['/home', '/usr/local', '/opt', '/var/www']
-            for q in exceptlist:
-                if os.path.commonprefix([mntpt, q]) == q:
-                    return 1
-            return 0
-
-        # check first to see if its a Linux filesystem or not
-        formatlist = ['/boot', '/var', '/tmp', '/usr']
-
-        if not self.fstype:
-            return 0
-
-        if not self.fstype.isLinuxNativeFS():
-            return 0
-
-        if self.fstype.isMountable():
-            mntpt = self.mountpoint
-            if mntpt == "/":
-                return 1
-
-            if mntpt in formatlist:
-                return 1
-
-            for p in formatlist:
-                if os.path.commonprefix([mntpt, p]) == p:
-                    if inExceptionList(mntpt):
-                        return 0
-                    else:
-                        return 1
-
-            return 0
-        else:
-            if self.fstype.getName() == "swap":
-                return 1
-
-        # be safe for anything else and default to off
-        return 0
-        
-
-# XXX preexistings store start/end as sectors, new store as cylinders. ICK
-class PartitionSpec(RequestSpec):
-    """Object to define a requested partition."""
-
-    # XXX eep, still a few too many options but a lot better
-    def __init__(self, fstype, size = None, mountpoint = None,
-                 preexist = 0, migrate = None, grow = 0, maxSizeMB = None,
-                 start = None, end = None, drive = None, primary = None,
-                 format = None, multidrive = None,
-                 fslabel = None, fsprofile=None):
-        """Create a new PartitionSpec object.
-
-        fstype is the fsset filesystem type.
-        size is the requested size (in megabytes).
-        mountpoint is the mountpoint.
-        grow is whether or not the partition is growable.
-        maxSizeMB is the maximum size of the partition in megabytes.
-        start is the starting cylinder/sector (new/preexist).
-        end is the ending cylinder/sector (new/preexist).
-        drive is the drive the partition goes on.
-        primary is whether or not the partition should be forced as primary.
-        format is whether or not the partition should be formatted.
-        preexist is whether this partition is preexisting.
-        migrate is whether or not the partition should be migrated.
-        multidrive specifies if this is a request that should be replicated
-            across _all_ of the drives in drive
-        fslabel is the label to give to the filesystem.
-        fsprofile is the usage profile for the filesystem.
-        """
-
-        # if it's preexisting, the original fstype should be set
-        if preexist == 1:
-            origfs = fstype
-        else:
-            origfs = None
-        
-        RequestSpec.__init__(self, fstype = fstype, size = size,
-                             mountpoint = mountpoint, format = format,
-                             preexist = preexist, migrate = None,
-                             origfstype = origfs,
-                             fslabel = fslabel, fsprofile = fsprofile)
-        self.type = REQUEST_NEW
-
-        self.grow = grow
-        self.maxSizeMB = maxSizeMB
-        self.requestSize = size
-        self.start = start
-        self.end = end
-
-        self.drive = drive
-        self.primary = primary
-        self.multidrive = multidrive
-
-        # should be able to map this from the device =\
-        self.currentDrive = None
-        """Drive that this request will currently end up on."""        
-
-
-    def __str__(self):
-        if self.fstype:
-            fsname = self.fstype.getName()
-        else:
-            fsname = "None"
-
-        if self.origfstype:
-            oldfs = self.origfstype.getName()
-        else:
-            oldfs = "None"
-
-        if self.preexist == 0:
-            pre = "New"
-        else:
-            pre = "Existing"
-
-        if self.encryption is None:
-            crypto = "None"
-        else:
-            crypto = self.encryption.getScheme()
-
-        str = ("%(n)s Part Request -- mountpoint: %(mount)s uniqueID: %(id)s\n"
-               "  type: %(fstype)s  format: %(format)s \n"
-               "  device: %(dev)s drive: %(drive)s  primary: %(primary)s\n"
-               "  size: %(size)s  grow: %(grow)s  maxsize: %(max)s\n"
-               "  start: %(start)s  end: %(end)s  migrate: %(migrate)s  "
-               "  fslabel: %(fslabel)s  origfstype: %(origfs)s\n"
-               "  options: '%(fsopts)s'\n"
-               "  fsprofile: %(fsprofile)s  encryption: %(encryption)s" % 
-               {"n": pre, "mount": self.mountpoint, "id": self.uniqueID,
-                "fstype": fsname, "format": self.format, "dev": self.device,
-                "drive": self.drive, "primary": self.primary,
-                "size": self.size, "grow": self.grow, "max": self.maxSizeMB,
-                "start": self.start, "end": self.end,
-                "migrate": self.migrate, "fslabel": self.fslabel,
-                "origfs": oldfs,
-                "fsopts": self.fsopts, "fsprofile": self.fsprofile,
-                "encryption": crypto})
-        return str
-
-
-    def getDevice(self, partitions):
-        """Return a device to solidify."""
-        if self.dev:
-            return self.dev
-
-        self.dev = fsset.PartitionDevice(self.device,
-                                         encryption = self.encryption)
-
-        return self.dev
-
-    def getActualSize(self, partitions, diskset):
-        """Return the actual size allocated for the request in megabytes."""
-        size = 0
-
-        for drive in self.drive:
-            part = diskset.disks[drive].getPartitionByPath("/dev/%s" % self.device)
-
-            if part:
-                size += part.getSize(unit="MB")
-
-        if size == 0:
-            return self.requestSize
-
-        return size
-
-    def doSizeSanityCheck(self):
-        """Sanity check that the size of the partition is sane."""
-        if not self.fstype:
-            return None
-        if not self.format:
-            return None
-        ret = RequestSpec.doSizeSanityCheck(self)
-        if ret is not None:
-            return ret
-
-        if (self.size and self.maxSizeMB
-            and (self.size > self.maxSizeMB)):
-            return (_("The size of the requested partition (size = %s MB) "
-                     "exceeds the maximum size of %s MB.")
-                    % (self.size, self.maxSizeMB))
-
-        if self.size and self.size < 0:
-            return _("The size of the requested partition is "
-                     "negative! (size = %s MB)") % (self.size)
-
-        if self.start and self.start < 1:
-            return _("Partitions can't start below the first cylinder.")
-
-        if self.end and self.end < 1:
-            return _("Partitions can't end on a negative cylinder.")
-
-        return None
-
-class NewPartitionSpec(PartitionSpec):
-    """Object to define a NEW requested partition."""
-
-    # XXX eep, still a few too many options but a lot better
-    def __init__(self, fstype, size = None, mountpoint = None,
-                 grow = 0, maxSizeMB = None,
-                 start = None, end = None,
-                 drive = None, primary = None, format = None):
-        """Create a new NewPartitionSpec object.
-
-        fstype is the fsset filesystem type.
-        size is the requested size (in megabytes).
-        mountpoint is the mountpoint.
-        grow is whether or not the partition is growable.
-        maxSizeMB is the maximum size of the partition in megabytes.
-        start is the starting cylinder.
-        end is the ending cylinder.
-        drive is the drive the partition goes on.
-        primary is whether or not the partition should be forced as primary.
-        format is whether or not the partition should be formatted.
-        """
-
-        PartitionSpec.__init__(self, fstype = fstype, size = size,
-                               mountpoint = mountpoint, grow = grow,
-                               maxSizeMB = maxSizeMB, start = start,
-                               end = end, drive = drive, primary = primary,
-                               format = format, preexist = 0)
-        self.type = REQUEST_NEW
-
-class PreexistingPartitionSpec(PartitionSpec):
-    """Request to represent partitions which already existed."""
-    
-    def __init__(self, fstype, size = None, start = None, end = None,
-                 drive = None, format = None, migrate = None,
-                 mountpoint = None):
-        """Create a new PreexistingPartitionSpec object.
-
-        fstype is the fsset filesystem type.
-        size is the size (in megabytes).
-        start is the starting sector.
-        end is the ending sector.
-        drive is the drive which the partition is on.
-        format is whether or not the partition should be formatted.
-        migrate is whether or not the partition fs should be migrated.
-        mountpoint is the mountpoint.
-        """
-
-        PartitionSpec.__init__(self, fstype = fstype, size = size,
-                               start = start, end = end, drive = drive,
-                               format = format, migrate = migrate,
-                               mountpoint = mountpoint, preexist = 1)
-        self.type = REQUEST_PREEXIST
-        self.resizable = True
-
-        self.maxResizeSize = None
-        """Maximum size of this partition request"""
-
-    def getMaximumResizeMB(self, partitions):
-        if self.maxResizeSize is not None:
-            return self.maxResizeSize
-        log.warning("%s doesn't have a max size set" %(self.device,))
-        return MAX_PART_SIZE
-
-    def getMinimumResizeMB(self, partitions):
-        return self.fstype.getMinimumSize(self.device)
-
-class RaidRequestSpec(RequestSpec):
-    """Request to represent RAID devices."""
-    
-    def __init__(self, fstype, format = None, mountpoint = None,
-                 raidlevel = None, raidmembers = None,
-                 raidspares = None, raidminor = None, fslabel = None,
-                 preexist = 0, chunksize = None,
-                 fsprofile = None):
-        """Create a new RaidRequestSpec object.
-
-        fstype is the fsset filesystem type.
-        format is whether or not the partition should be formatted.
-        mountpoint is the mountpoint.
-        raidlevel is the raidlevel (as 'RAID0', 'RAID1', 'RAID5').
-        chunksize is the chunksize which should be used.
-        raidmembers is list of ids corresponding to the members of the RAID.
-        raidspares is the number of spares to setup.
-        raidminor is the minor of the device which should be used.
-        fslabel is the label of the filesystem.
-        fsprofile is the usage profile for the filesystem.
-        """
-
-        # if it's preexisting, the original fstype should be set
-        if preexist == 1:
-            origfs = fstype
-        else:
-            origfs = None
-
-        RequestSpec.__init__(self, fstype = fstype, format = format,
-                             mountpoint = mountpoint, preexist = preexist,
-                             origfstype = origfs,
-                             fslabel=fslabel, fsprofile=fsprofile)
-        self.type = REQUEST_RAID
-        
-
-        self.raidlevel = raidlevel
-        self.raidmembers = raidmembers
-        self.raidspares = raidspares
-        self.raidminor = raidminor
-        self.chunksize = chunksize
-
-    def __str__(self):
-        if self.fstype:
-            fsname = self.fstype.getName()
-        else:
-            fsname = "None"
-        raidmem = []
-        if self.raidmembers:
-            for i in self.raidmembers:
-                raidmem.append(i)
-
-        if self.encryption is None:
-            crypto = "None"
-        else:
-            crypto = self.encryption.getScheme()
-                
-        str = ("RAID Request -- mountpoint: %(mount)s  uniqueID: %(id)s\n"
-               "  type: %(fstype)s  format: %(format)s\n"
-               "  raidlevel: %(level)s  raidspares: %(spares)s\n"
-               "  raidmembers: %(members)s  fsprofile: %(fsprofile)s\n"
-               "  encryption: %(encryption)s" % 
-               {"mount": self.mountpoint, "id": self.uniqueID,
-                "fstype": fsname, "format": self.format,
-                "level": self.raidlevel, "spares": self.raidspares,
-                "members": self.raidmembers, "fsprofile": self.fsprofile,
-                "encryption": crypto
-                })
-        return str
-    
-    def getDevice(self, partitions):
-        """Return a device which can be solidified."""
-        # Alway return a new device for minor changing
-        raidmems = []
-        for member in self.raidmembers:
-            request = partitions.getRequestByID(member)
-            raidmems.append(request.getDevice(partitions))
-        self.dev = fsset.RAIDDevice(int(self.raidlevel[4:]),
-                                    raidmems, minor = self.raidminor,
-                                    spares = self.raidspares,
-                                    existing = self.preexist,
-                                    chunksize = self.chunksize,
-                                    encryption = self.encryption)
-        return self.dev
-
-    def isEncrypted(self, partitions, parentOnly = False):
-        if RequestSpec.isEncrypted(self, partitions) is True:
-            return True
-        if parentOnly:
-            return False
-        for member in self.raidmembers:
-            if partitions.getRequestByID(member).isEncrypted(partitions):
-                return True
-        return False
-
-    def getActualSize(self, partitions, diskset):
-        """Return the actual size allocated for the request in megabytes."""
-
-        # this seems like a check which should never fail...
-        if not self.raidmembers or not self.raidlevel:
-            return 0
-        nummembers = len(self.raidmembers) - self.raidspares
-        smallest = None
-        sum = 0
-        for member in self.raidmembers:
-            req = partitions.getRequestByID(member)
-            partsize = req.getActualSize(partitions, diskset)
-
-            if raid.isRaid0(self.raidlevel):
-                sum = sum + partsize
-            else:
-                if not smallest:
-                    smallest = partsize
-                elif partsize < smallest:
-                    smallest = partsize
-
-        if raid.isRaid0(self.raidlevel):
-            return sum
-        elif raid.isRaid1(self.raidlevel):
-            return smallest
-        elif raid.isRaid5(self.raidlevel):
-            return (nummembers-1) * smallest
-        elif raid.isRaid6(self.raidlevel):
-            return (nummembers-2) * smallest
-        elif raid.isRaid10(self.raidlevel):
-            return (nummembers/2) * smallest
-        else:
-            raise ValueError, "Invalid raidlevel in RaidRequest.getActualSize"
-        
-
-    # do RAID specific sanity checks; this is an internal function
-    def sanityCheckRaid(self, partitions):
-        if not self.raidmembers or not self.raidlevel:
-            return _("No members in RAID request, or not RAID "
-                     "level specified.")
-
-        minmembers = raid.get_raid_min_members(self.raidlevel)
-        if len(self.raidmembers) < minmembers:
-            return _("A RAID device of type %s "
-                     "requires at least %s members.") % (self.raidlevel,
-                                                         minmembers)
-
-        if len(self.raidmembers) > 27:
-            return "RAID devices are limited to 27 members."
-
-        if self.raidspares:
-            if (len(self.raidmembers) - self.raidspares) < minmembers:
-                return _("This RAID device can have a maximum of %s spares. "
-                         "To have more spares you will need to add members to "
-                         "the RAID device.") % (len(self.raidmembers)
-                                                - minmembers )
-        return None
-
-    def sanityCheckRequest(self, partitions):
-        """Run the basic sanity checks on the request."""
-        rc = self.sanityCheckRaid(partitions)
-        if rc:
-            return rc
-
-        return RequestSpec.sanityCheckRequest(self, partitions)
-
-class VolumeGroupRequestSpec(RequestSpec):
-    """Request to represent volume group devices."""
-    
-    def __init__(self, fstype =None, format = None,
-                 vgname = None, physvols = None,
-                 pesize = 32768, preexist = 0,
-                 preexist_size = 0):
-        """Create a new VolumeGroupRequestSpec object.
-
-        fstype is the fsset filesystem type.
-        format is whether or not the volume group should be created.
-        vgname is the name of the volume group.
-        physvols is a list of the ids for the physical volumes in the vg.
-        pesize is the size of a physical extent in kilobytes.
-        preexist is whether the volume group is preexisting.
-        preexist_size is the size of a preexisting VG read from /proc
-            (note that this is unclamped)
-        """
-
-        if not fstype:
-            fstype = fsset.fileSystemTypeGet("volume group (LVM)")
-        RequestSpec.__init__(self, fstype = fstype, format = format)
-        self.type = REQUEST_VG
-
-        self.volumeGroupName = vgname
-        self.physicalVolumes = physvols
-        self.pesize = pesize
-        self.preexist = preexist
-        self.free = 0
-
-        # FIXME: this is a hack so that we can set the vg name automagically
-        # with autopartitioning to not conflict with existing vgs
-        self.autoname = 0
-
-        if preexist and preexist_size:
-            self.preexist_size = preexist_size
-        else:
-            self.preexist_size = None
-
-    def __str__(self):
-        physvols = []
-        if self.physicalVolumes:
-            for i in self.physicalVolumes:
-                physvols.append(i)
-                
-        str = ("VG Request -- name: %(vgname)s  uniqueID: %(id)s\n"
-               "  format: %(format)s pesize: %(pesize)s  \n"
-               "  physvols: %(physvol)s" %
-               {"vgname": self.volumeGroupName, "id": self.uniqueID,
-                "format": self.format, "physvol": physvols,
-                "pesize": self.pesize})
-        return str
-    
-    def getDevice(self, partitions):
-        """Return a device which can be solidified."""
-        if self.dev:
-            # FIXME: this warning can probably be removed post-beta            
-            log.warning("getting self.dev more than once for %s" %(self,))
-            return self.dev
-        
-        pvs = []
-        for pv in self.physicalVolumes:
-            r = partitions.getRequestByID(pv)
-            # a size of zero implies we did autopartitioning of
-            # pvs everywhere we could
-            if (r.size > 0) or (r.device is not None):
-                pvs.append(r.getDevice(partitions))
-        self.dev = fsset.VolumeGroupDevice(self.volumeGroupName, pvs,
-                                           self.pesize,
-                                           existing = self.preexist)
-        return self.dev
-
-    def isEncrypted(self, partitions, parentOnly = False):
-        if RequestSpec.isEncrypted(self, partitions) is True:
-            return True
-        if parentOnly:
-            return False
-        for pvid in self.physicalVolumes:
-            pv = partitions.getRequestByID(pvid)
-            if pv.isEncrypted(partitions):
-                return True
-        return False
-
-    def getActualSize(self, partitions, diskset):
-        """Return the actual size allocated for the request in megabytes."""
-
-        # if we have a preexisting size, use it
-        if self.preexist and self.preexist_size:
-            totalspace = lvm.clampPVSize(self.preexist_size, self.pesize)
-        else:
-            totalspace = 0
-            for pvid in self.physicalVolumes:
-                pvreq = partitions.getRequestByID(pvid)
-                size = pvreq.getActualSize(partitions, diskset)
-                #log.info("size for pv %s is %s" % (pvid, size))
-                size = lvm.clampPVSize(size, self.pesize) - (self.pesize/1024)
-                #log.info("  clamped size is %s" % (size,))
-                totalspace = totalspace + size
-
-        return totalspace
-
-class PartialVolumeGroupSpec:
-    """Request to represent partial volume group devices."""
-    # note, these are just used as placeholders so we don't collide on names
-    
-    def __init__(self, vgname = None):
-        """Create a new PartialVolumeGroupSpec object.
-
-        vgname is the name of the volume group.
-        """
-
-        self.volumeGroupName = vgname
-
-    def __str__(self):
-        str = ("Partial VG Request -- name: %(vgname)s" %
-               {"vgname": self.volumeGroupName})
-        return str
-    
-class LogicalVolumeRequestSpec(RequestSpec):
-    """Request to represent logical volume devices."""
-    
-    def __init__(self, fstype, format = None, mountpoint = None,
-                 size = None, volgroup = None, lvname = None,
-                 preexist = 0, percent = None, grow=0, maxSizeMB=0,
-		 fslabel = None, fsprofile = None):
-        """Create a new VolumeGroupRequestSpec object.
-
-        fstype is the fsset filesystem type.
-        format is whether or not the volume group should be created.
-        mountpoint is the mountpoint for the request.
-        size is the size of the request in MB.
-        volgroup is the request ID of the volume group.
-        lvname is the name of the logical volume.
-        preexist is whether the logical volume previously existed or not.
-        percent is the percentage of the volume group's space this should use.
-	grow is whether or not to use free space remaining.
-	maxSizeMB is max size to grow to.
-        fslabel is the label of the filesystem on the logical volume.
-        fsprofile is the usage profile for the filesystem.
-        """
-
-        # if it's preexisting, the original fstype should be set
-        if preexist == 1:
-            origfs = fstype
-        else:
-            origfs = None
-
-	RequestSpec.__init__(self, fstype = fstype, format = format,
-			     mountpoint = mountpoint, size = size,
-			     preexist = preexist, origfstype = origfs,
-			     fslabel = fslabel, fsprofile = fsprofile)
-	    
-        self.type = REQUEST_LV
-
-        self.logicalVolumeName = lvname
-        self.volumeGroup = volgroup
-        self.percent = percent
-        self.grow = grow
-        self.maxSizeMB = maxSizeMB
-        self.startSize = size
-
-        self.minResizeSize = None
-        self.resizable = True
-	
-        if not percent and not size and not preexist:
-            raise RuntimeError, "Error with Volume Group:Logical Volume %s:%s - Logical Volume must specify either percentage of vgsize or size" % (volgroup, lvname)
-
-	if percent and grow:
-            raise RuntimeError, "Error with Volume Group:Logical Volume %s:%s - Logical Volume cannot grow if percentage given" % (volgroup, lvname)
-
-    def __str__(self):
-        if self.fstype:
-            fsname = self.fstype.getName()
-        else:
-            fsname = "None"
-
-        if self.size is not None:
-            size = self.size
-        else:
-            size = "%s percent" %(self.percent,)
-        
-        if self.encryption is None:
-            crypto = "None"
-        else:
-            crypto = self.encryption.getScheme()
-
-        str = ("LV Request -- mountpoint: %(mount)s  uniqueID: %(id)s\n"
-               "  type: %(fstype)s  format: %(format)s\n"
-               "  size: %(size)s  lvname: %(lvname)s  volgroup: %(vgid)s\n"
-               "  options: '%(fsopts)s'  fsprofile: %(fsprofile)s"
-               "  encryption: '%(crypto)s'" %
-               {"mount": self.mountpoint, "id": self.uniqueID,
-                "fstype": fsname, "format": self.format,
-                "lvname": self.logicalVolumeName, "vgid": self.volumeGroup,
-		"size": size, "crypto": crypto,
-                "fsopts": self.fsopts, "fsprofile": self.fsprofile})
-        return str
-    
-    def getDevice(self, partitions):
-        """Return a device which can be solidified."""
-        vg = partitions.getRequestByID(self.volumeGroup)
-        vgname = vg.volumeGroupName
-        self.dev = fsset.LogicalVolumeDevice(vgname, self.size,
-                                             self.logicalVolumeName,
-                                             vg = vg,
-                                             existing = self.preexist,
-                                             encryption = self.encryption)
-        return self.dev
-
-    def isEncrypted(self, partitions, parentOnly = False):
-        if RequestSpec.isEncrypted(self, partitions) is True:
-            return True
-        if parentOnly:
-            return False
-        vg = partitions.getRequestByID(self.volumeGroup)
-        if vg.isEncrypted(partitions):
-            return True
-        return False
-
-    def getActualSize(self, partitions = None, diskset = None, target = False):
-        """Return the actual size allocated for the request in megabytes."""
-        if self.percent:
-            if partitions is None or diskset is None:
-                raise RuntimeError, "trying to get a percentage lv size on resize path"
-            vgreq = partitions.getRequestByID(self.volumeGroup)
-	    vgsize = vgreq.getActualSize(partitions, diskset)
-	    lvsize = int(self.percent * 0.01 * vgsize)
-	    #lvsize = lvm.clampLVSizeRequest(lvsize, vgreq.pesize)
-            return lvsize
-        # FIXME: the target bit here is a bit of a hack...
-        elif self.targetSize is not None and target:
-            return self.targetSize
-        else:
-            return self.size
-
-    def getStartSize(self):
-        """Return the starting size allocated for the request in megabytes."""
-	return self.startSize
-
-    def setSize(self, size):
-	"""Set the size (in MB) of request (does not clamp to PE however)
-
-	size - size in MB
-	"""
-	if self.percent:
-	    self.percent = None
-
-	self.size = size
-
-    def sanityCheckRequest(self, partitions, skipMntPtExistCheck=0, pesize=32768):
-        """Run the basic sanity checks on the request."""
-        if not self.grow and not self.percent and self.size*1024 < pesize:
-            return _("Logical volume size must be larger than the volume "
-                     "group's physical extent size.")
-
-        return RequestSpec.sanityCheckRequest(self, partitions, skipMntPtExistCheck)
-
-    def getMaximumResizeMB(self, partitions):
-        vg = partitions.getRequestByID(self.volumeGroup)
-        print("max is", self.getActualSize(), vg.free, self.getActualSize() + vg.free)
-        return self.getActualSize() + vg.free
-
-    def getMinimumResizeMB(self, partitions):
-        if self.minResizeSize is None:
-            log.warning("don't know the minimum size of %s" %(self.logicalVolumeName,))
-            return 1
-        return self.minResizeSize
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/raid.py anaconda-storage/raid.py
--- anaconda/raid.py	2009-02-13 23:03:10.000000000 -0600
+++ anaconda-storage/raid.py	1969-12-31 18:00:00.000000000 -0600
@@ -1,228 +0,0 @@
-#
-# raid.py - raid probing control
-#
-# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
-# Red Hat, Inc.  All rights reserved.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-#
-# Author(s): Erik Troan <ewt@redhat.com>
-#
-
-"""Raid probing control."""
-
-def getRaidLevels():
-    avail = []
-    try:
-        f = open("/proc/mdstat", "r")
-    except:
-        pass
-    else:
-        for l in f.readlines():
-            if not l.startswith("Personalities"):
-                continue
-
-            lst = l.split()
-
-            for lev in ["RAID0", "RAID1", "RAID5", "RAID6", "RAID10"]:
-                if "[" + lev + "]" in lst or "[" + lev.lower() + "]" in lst:
-                    avail.append(lev)
-
-        f.close()
-
-    avail.sort()
-    return avail
-
-# XXX define availraidlevels and defaultmntpts as arch characteristics
-availRaidLevels = getRaidLevels()
-
-import parted
-import isys
-import os
-import partitions
-import partedUtils
-
-import logging
-log = logging.getLogger("anaconda")
-
-# these arches can have their /boot on RAID and not have their
-# boot loader blow up
-raidBootArches = [ "i386", "x86_64", "ppc" ]
-
-def scanForRaid(drives):
-    """Scans for raid devices on drives.
-
-    drives is a list of device names.
-    Returns a list of (mdMinor, devices, level, totalDisks) tuples.
-    """
-
-    raidSets = {}
-    raidDevices = {}
-
-    for d in drives:
-        parts = []
-        try:
-            dev = parted.getDevice("/dev/%s" % (d,))
-            disk = parted.Disk(device=dev)
-
-            raidParts = disk.getRaidPartitions()
-            for part in raidParts:
-                # if the part is encrypted, add the mapped dev instead
-                pname = part.getDeviceNodeName()
-                cryptoDev = partitions.Partitions.encryptedDevices.get(pname)
-                if cryptoDev and not cryptoDev.openDevice():
-                    dev = cryptoDev.getDevice()
-                else:
-                    dev = pname
-                parts.append(dev)
-        except:
-            pass
-
-        for dev in parts:
-            try:
-                (major, minor, raidSet, level, nrDisks, totalDisks, mdMinor) =\
-                        isys.raidsb(dev)
-            except ValueError:
-                # bad magic, this can't be part of our raid set
-                log.error("reading raid sb failed for %s",dev)
-                continue
-
-	    if raidSets.has_key(raidSet):
-	    	(knownLevel, knownDisks, knownMinor, knownDevices) = \
-			raidSets[raidSet]
-		if knownLevel != level or knownDisks != totalDisks or \
-		   knownMinor != mdMinor:
-                    # Raise hell
-		    log.error("raid set inconsistency for md%d: "
-                              "all drives in this raid set do not "
-                              "agree on raid parameters.  Skipping raid device",
-                              mdMinor)
-                    continue
-		knownDevices.append(dev)
-		raidSets[raidSet] = (knownLevel, knownDisks, knownMinor,
-				     knownDevices)
-	    else:
-		raidSets[raidSet] = (level, totalDisks, mdMinor, [dev,])
-
-	    if raidDevices.has_key(mdMinor):
-	    	if (raidDevices[mdMinor] != raidSet):
-		    log.error("raid set inconsistency for md%d: "
-                              "found members of multiple raid sets "
-                              "that claim to be md%d.  Using only the first "
-                              "array found.", mdMinor, mdMinor)
-                    continue
-	    else:
-	    	raidDevices[mdMinor] = raidSet
-
-    raidList = []
-    for key in raidSets.keys():
-	(level, totalDisks, mdMinor, devices) = raidSets[key]
-	if len(devices) < totalDisks:
-            log.warning("missing components of raid device md%d.  The "
-                        "raid device needs %d drive(s) and only %d (was/were) "
-                        "found. This raid device will not be started.", mdMinor,
-                        totalDisks, len(devices))
-	    continue
-	raidList.append((mdMinor, devices, level, totalDisks))
-
-    return raidList
-		
-def startAllRaid(driveList):
-    """Do a raid start on raid devices and return a list like scanForRaid."""
-    rc = []
-    mdList = scanForRaid(driveList)
-    for mdDevice, deviceList, level, numActive in mdList:
-    	devName = "md%d" % (mdDevice,)
-	isys.raidstart(devName, deviceList[0])
-        rc.append((devName, deviceList, level, numActive))
-    return rc
-
-def stopAllRaid(mdList):
-    """Do a raid stop on each of the raid device tuples given."""
-    for dev, devices, level, numActive in mdList:
-	isys.raidstop(dev)
-
-def isRaid10(raidlevel):
-    """Return whether raidlevel is a valid descriptor of RAID10."""
-    if raidlevel in ("RAID10", "10", 10):
-        return True
-    return False
-
-def isRaid6(raidlevel):
-    """Return whether raidlevel is a valid descriptor of RAID6."""
-    if raidlevel in ("RAID6", "6", 6):
-        return True
-    return False
-
-def isRaid5(raidlevel):
-    """Return whether raidlevel is a valid descriptor of RAID5."""
-    if raidlevel in ("RAID5", "5", 5):
-        return True
-    return False
-
-def isRaid1(raidlevel):
-    """Return whether raidlevel is a valid descriptor of RAID1."""
-    if raidlevel in ("mirror", "RAID1", "1", 1):
-        return True
-    return False
-
-def isRaid0(raidlevel):
-    """Return whether raidlevel is a valid descriptor of RAID0."""
-    if raidlevel in ("stripe", "RAID0", "0", 0):
-        return True
-    return False
-
-def get_raid_min_members(raidlevel):
-    """Return the minimum number of raid members required for raid level"""
-    if isRaid0(raidlevel):
-        return 2
-    elif isRaid1(raidlevel):
-        return 2
-    elif isRaid5(raidlevel):
-        return 3
-    elif isRaid6(raidlevel):
-        return 4
-    elif isRaid10(raidlevel):
-        return 4
-    else:
-        raise ValueError, "invalid raidlevel in get_raid_min_members"
-
-def get_raid_max_spares(raidlevel, nummembers):
-    """Return the maximum number of raid spares for raidlevel."""
-    if isRaid0(raidlevel):
-        return 0
-    elif isRaid1(raidlevel) or isRaid5(raidlevel) or isRaid6(raidlevel) or isRaid10(raidlevel):
-        return max(0, nummembers - get_raid_min_members(raidlevel))
-    else:
-        raise ValueError, "invalid raidlevel in get_raid_max_spares"
-
-def register_raid_device(mdname, newdevices, newlevel, newnumActive):
-    """Register a new RAID device in the mdlist."""
-    for dev, devices, level, numActive in partedUtils.DiskSet.mdList:
-        if mdname == dev:
-            if (devices != newdevices or level != newlevel or numActive != newnumActive):
-                raise ValueError, "%s is already in the mdList!" % (mdname,)
-            else:
-                return
-    partedUtils.DiskSet.mdList.append((mdname, newdevices[:], newlevel,
-                                       newnumActive))
-
-def lookup_raid_device(mdname):
-    """Return the requested RAID device information."""
-    for dev, devices, level, numActive in partedUtils.DiskSet.mdList:
-        if mdname == dev:
-            return (dev, devices, level, numActive)
-    raise KeyError, "md device not found"
-
-
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/rescue.py anaconda-storage/rescue.py
--- anaconda/rescue.py	2009-02-13 23:03:10.000000000 -0600
+++ anaconda-storage/rescue.py	2009-02-15 13:42:50.000000000 -0600
@@ -101,14 +101,17 @@
 # XXX grub-install is stupid and uses df output to figure out
 # things when installing grub.  make /etc/mtab be at least
 # moderately useful.  
-def makeMtab(instPath, theFsset):
-    child = os.fork()
-    if (not child):
-        os.chroot(instPath)
+def makeMtab(instPath, fsset):
+    try:
         f = open("/etc/mtab", "w+")
-        f.write(theFsset.mtab())
+    except IOError, e:
+        log.info("failed to open /etc/mtab for write: %s" % e)
+        return
+
+    try:
+        f.write(fsset.mtab())
+    finally:
         f.close()
-        os._exit(0)
 
 # make sure they have a resolv.conf in the chroot
 def makeResolvConf(instPath):
@@ -265,17 +268,14 @@
         else:
             scroll = 0
 
-        partList = []
-        for (drive, fs, relstr, label, uuid) in disks:
-            if label:
-                partList.append("%s (%s)" % (drive, label))
-            else:
-                partList.append(drive)
+        devList = []
+        for (device, relstr) in disks:
+            devList.append(device.path)
 
         (button, choice) = \
             ListboxChoiceWindow(screen, _("System to Rescue"),
-                                _("What partition holds the root partition "
-                                  "of your installation?"), partList,
+                                _("Which device holds the root partition "
+                                  "of your installation?"), devList,
                                 [ _("OK"), _("Exit") ], width = 30,
                                 scroll = scroll, height = height,
                                 help = "multipleroot")
@@ -283,19 +283,16 @@
         if button == string.lower (_("Exit")):
             root = None
         else:
-            root = disks[choice]
+            root = disks[choice][0]
 
     rootmounted = 0
 
     if root:
         try:
-            fs = fsset.FileSystemSet()
-
-            # only pass first two parts of tuple for root, since third
-            # element is a comment we dont want
-            rc = upgrade.mountRootPartition(anaconda, root[:2], fs,
-                                            allowDirty = 1, warnDirty = 1,
-                                            readOnly = readOnly)
+            fsset = FSSet(anaconda.id.devicetree)
+            rc = mountExistingSystem(anaconda, root, fsset,
+                                     allowDirty = 1, warnDirty = 1,
+                                     readOnly = readOnly)
 
             if rc == -1:
                 if anaconda.isKickstart:
@@ -325,7 +322,7 @@
                 # now turn on swap
                 if not readOnly:
                     try:
-                        fs.turnOnSwap("/")
+                        fsset.turnOnSwap("/")
                     except:
                         log.error("Error enabling swap")
 
@@ -412,7 +409,7 @@
     msgStr = ""
 
     if rootmounted and not readOnly:
-        makeMtab(anaconda.rootPath, fs)
+        makeMtab(anaconda.rootPath, fsset)
         try:
             makeResolvConf(anaconda.rootPath)
         except Exception, e:
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/storage/deviceaction.py anaconda-storage/storage/deviceaction.py
--- anaconda/storage/deviceaction.py	1969-12-31 18:00:00.000000000 -0600
+++ anaconda-storage/storage/deviceaction.py	2009-02-12 16:17:59.000000000 -0600
@@ -0,0 +1,293 @@
+# deviceaction.py
+# Device modification action classes for anaconda's storage configuration
+# module.
+#
+# Copyright (C) 2009  Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
+# Public License for more details.  You should have received a copy of the
+# GNU General Public License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.  Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): Dave Lehman <dlehman@redhat.com>
+#
+""" action.py
+
+"""
+from devices import StorageDevice
+from errors import *
+
+import gettext
+_ = lambda x: gettext.ldgettext("anaconda", x)
+
+import logging
+log = logging.getLogger("storage")
+
+
+""" The values are just hints as to the ordering.
+
+    Eg: fsmod and devmod ordering depends on the mod (shrink -v- grow)
+"""
+ACTION_TYPE_NONE = 0
+ACTION_TYPE_DESTROY = 1000
+ACTION_TYPE_RESIZE = 500
+ACTION_TYPE_CREATE = 250
+
+action_strings = {ACTION_TYPE_NONE: "None",
+                  ACTION_TYPE_DESTROY: "Destroy",
+                  ACTION_TYPE_RESIZE: "Resize",
+                  ACTION_TYPE_CREATE: "Create"}
+
+ACTION_OBJECT_NONE = 0
+ACTION_OBJECT_FORMAT = 1
+ACTION_OBJECT_DEVICE = 2
+
+object_strings = {ACTION_OBJECT_NONE: "None",
+                  ACTION_OBJECT_FORMAT: "Format",
+                  ACTION_OBJECT_DEVICE: "Device"}
+
+RESIZE_SHRINK = 88
+RESIZE_GROW = 89
+
+resize_strings = {RESIZE_SHRINK: "Shrink",
+                  RESIZE_GROW: "Grow"}
+
+class DeviceAction(object):
+    """ An action that will be carried out in the future on a Device.
+
+        These classes represent actions to be performed on devices or
+        filesystems.
+
+        The operand Device instance will be modified according to the
+        action, but no changes will be made to the underlying device or
+        filesystem until the DeviceAction instance's execute method is
+        called. The DeviceAction instance's cancel method should reverse
+        any modifications made to the Device instance's attributes.
+
+        If the Device instance represents a pre-existing device, the
+        constructor should call any methods or set any attributes that the
+        action will eventually change. Device/DeviceFormat classes should verify
+        that the requested modifications are reasonable and raise an
+        exception if not.
+
+        Only one action of any given type/object pair can exist for any
+        given device at any given time. This is enforced by the
+        DeviceTree.
+
+        Basic usage:
+
+            a = DeviceAction(dev)
+            a.execute()
+
+            OR
+
+            a = DeviceAction(dev)
+            a.cancel()
+
+
+        XXX should we back up the device with a deep copy for forcibly
+            cancelling actions?
+
+            The downside is that we lose any checking or verification that
+            would get done when resetting the Device instance's attributes to
+            their original values.
+
+            The upside is that we would be guaranteed to achieve a total
+            reversal. No chance of, eg: resizes ending up altering Device
+            size due to rounding or other miscalculation.
+"""
+    type = ACTION_TYPE_NONE
+    obj = ACTION_OBJECT_NONE
+
+    def __init__(self, device):
+        if not isinstance(device, StorageDevice):
+            raise ValueError("arg 1 must be a StorageDevice instance")
+        self.device = device
+        #self._backup = deepcopy(device)
+
+    def execute(self):
+        """ perform the action """
+        pass
+
+    def cancel(self):
+        """ cancel the action """
+        pass
+
+    def isDestroy(self):
+        return self.type == ACTION_TYPE_DESTROY
+
+    def isCreate(self):
+        return self.type == ACTION_TYPE_CREATE
+
+    def isResize(self):
+        return self.type == ACTION_TYPE_RESIZE
+
+    def isShrink(self):
+        return (self.type == ACTION_TYPE_RESIZE and self.dir == RESIZE_SHRINK)
+
+    def isGrow(self):
+        return (self.type == ACTION_TYPE_RESIZE and self.dir == RESIZE_GROW)
+
+    def isDevice(self):
+        return self.obj == ACTION_OBJECT_DEVICE
+
+    def isFormat(self):
+        return self.obj == ACTION_OBJECT_FORMAT
+
+    def __str__(self):
+        s = "%s %s" % (action_strings[self.type], object_strings[self.obj])
+        if self.isResize():
+            s += " (%s)" % resize_strings[self.dir]
+        if self.isFormat():
+            if self.format:
+                fmt_type = self.format.type
+            else:
+                fmt_type = None
+            s += " %s on" % fmt_type
+        s += " %s (%s)" % (self.device.name, self.device.type)
+        return s
+
+class ActionCreateDevice(DeviceAction):
+    """ Action representing the creation of a new device. """
+    type = ACTION_TYPE_CREATE
+    obj = ACTION_OBJECT_DEVICE
+
+    def __init__(self, device):
+        # FIXME: assert device.fs is None
+        DeviceAction.__init__(self, device)
+
+    def execute(self):
+        self.device.create()
+
+
+class ActionDestroyDevice(DeviceAction):
+    """ An action representing the deletion of an existing device. """
+    type = ACTION_TYPE_DESTROY
+    obj = ACTION_OBJECT_DEVICE
+
+    def __init__(self, device):
+        # XXX should we insist that device.fs be None?
+        DeviceAction.__init__(self, device)
+
+    def execute(self):
+        self.device.destroy()
+
+
+class ActionResizeDevice(DeviceAction):
+    """ An action representing the resizing of an existing device. """
+    type = ACTION_TYPE_RESIZE
+    obj = ACTION_OBJECT_DEVICE
+
+    def __init__(self, device, newsize):
+        if device.size == newsize:
+            raise ValueError("new size same as old size")
+
+        DeviceAction.__init__(self, device)
+        if newsize > device.size:
+            self.dir = RESIZE_GROW
+        else:
+            self.dir = RESIZE_SHRINK
+        self.origsize = device.size
+        self.device.size = newsize
+
+    def execute(self):
+        self.device.resize()
+
+    def cancel(self):
+        self.device.size = self.origsize
+
+
+class ActionCreateFormat(DeviceAction):
+    """ An action representing creation of a new filesystem. """
+    type = ACTION_TYPE_CREATE
+    obj = ACTION_OBJECT_FORMAT
+
+    def __init__(self, device, format):
+        DeviceAction.__init__(self, device)
+        # should we insist that device.format be None?
+        # (indicative of ActionDestroyFormat having been instantiated)
+        self.origFormat = device.format
+        self.device.teardownFormat()
+        self.device.setFormat(format)
+
+    def execute(self):
+        # XXX we should set partition type flag as needed
+        #     - or should that be in the CreateDevice action?
+        self.device.setup()
+        self.device.createFormat()
+
+    def cancel(self):
+        self.device.setFormat(self.origFormat)
+
+    @property
+    def format(self):
+        return self.device.format
+
+
+class ActionDestroyFormat(DeviceAction):
+    """ An action representing the removal of an existing filesystem.
+
+        XXX this seems unnecessary
+    """
+    type = ACTION_TYPE_DESTROY
+    obj = ACTION_OBJECT_FORMAT
+
+    def __init__(self, device):
+        DeviceAction.__init__(self, device)
+        self.origFormat = device.format
+        if device.format and device.format.status:
+            device.format.teardown()
+        self.device.setFormat(None)
+
+    def execute(self):
+        """ wipe the filesystem signature from the device """
+        #self.device.destroyFormat()
+        if self.origFormat:
+            self.device.setup()
+            self.origFormat.destroy()
+
+    def cancel(self):
+        self.device.setFormat(self.origFormat)
+
+    @property
+    def format(self):
+        return self.origFormat
+
+
+class ActionResizeFormat(DeviceAction):
+    """ An action representing the resizing of an existing filesystem.
+
+        XXX Do we even want to support resizing of a filesystem without
+            also resizing the device it resides on?
+    """
+    type = ACTION_TYPE_RESIZE
+    obj = ACTION_OBJECT_FORMAT
+
+    def __init__(self, device, newsize):
+        if device.size == newsize:
+            raise ValueError("new size same as old size")
+
+        DeviceAction.__init__(self, device)
+        if newsize > device.format.size:
+            self.dir = RESIZE_GROW
+        else:
+            self.dir = RESIZE_SHRINK
+        self.device.format.setSize(newsize)
+
+    def execute(self):
+        self.device.format.resize()
+
+    def cancel(self):
+        self.device.format.setSize(None)
+
+
+
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/storage/devicelibs/crypto.py anaconda-storage/storage/devicelibs/crypto.py
--- anaconda/storage/devicelibs/crypto.py	1969-12-31 18:00:00.000000000 -0600
+++ anaconda-storage/storage/devicelibs/crypto.py	2009-02-12 16:17:59.000000000 -0600
@@ -0,0 +1,196 @@
+#
+# crypto.py
+#
+# Copyright (C) 2009  Red Hat, Inc.  All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+# Author(s): Dave Lehman <dlehman@redhat.com>
+#
+
+import iutil
+import os
+
+import gettext
+_ = lambda x: gettext.ldgettext("anaconda", x)
+
+class CryptoError(Exception):
+    pass
+
+def is_luks(device):
+    rc = iutil.execWithRedirect("cryptsetup",
+                                ["isLuks", device],
+                                stdout = "/dev/null",
+                                stderr = "/dev/null",
+                                searchPath = 1)
+    if rc:
+        return False
+    else:
+        return True
+
+def luks_uuid(device):
+    uuid = iutil.execWithCapture("cryptsetup",
+                                 ["luksUUID", device],
+                                 stderr="/dev/null")
+    return uuid.strip()
+
+def luks_status(name):
+    """0 means active, 1 means inactive (or non-existent)"""
+    rc = iutil.execWithRedirect("cryptsetup",
+                                ["status", name],
+                                stdout = "/dev/null",
+                                stderr = "/dev/null",
+                                searchPath = 1)
+    return rc
+
+def luks_format(device,
+                passphrase=None, key_file=None,
+                cipher=None, key_size=None):
+    p = os.pipe()
+    argv = ["-q"]
+    os.close(p[1])
+
+    if cipher:
+        argv.extend(["--cipher", cipher])
+
+    if key_size:
+        argv.append("--key-size=%d" % key_size)
+
+    argv.extend(["luksFormat", device])
+        
+    if passphrase:
+        os.write(p[1], "%s\n" % passphrase)
+    elif key_file and os.path.isfile(key_file):
+        argv.append(key_file)
+    else:
+        raise ValueError("luks_format requires either a passphrase or a key file")
+
+    rc = iutil.execWithRedirect("cryptsetup",
+                                argv,
+                                stdin = p[0],
+                                stdout = "/dev/null",
+                                stderr = "/dev/null",
+                                searchPath = 1)
+
+    os.close(p[0])
+    if rc:
+        raise CryptoError("luks_format failed for '%s'" % device)
+
+def luks_open(device, name, passphrase=None, key_file=None):
+    p = os.pipe()
+    if passphrase:
+        os.write(p[1], "%s\n" % passphrase)
+        argv = ["luksOpen", device, name]
+    elif key_file and os.path.isfile(key_file):
+        argv = ["luksOpen", "--key-file", key_file, device, name]
+    else:
+        raise ValueError("luks_open requires either a passphrase or a key file")
+
+    os.close(p[1])
+    rc = iutil.execWithRedirect("cryptsetup",
+                                argv,
+                                stdin = p[0],
+                                stdout = "/dev/null",
+                                stderr = "/dev/null",
+                                searchPath = 1)
+
+    os.close(p[0])
+    if rc:
+        raise CryptoError("luks_open failed for %s (%s)" % (device, name))
+
+def luks_close(name):
+    rc = iutil.execWithRedirect("cryptsetup",
+                                ["luksClose", name],
+                                stdout = "/dev/null",
+                                stderr = "/dev/null",
+                                searchPath = 1)
+
+    if rc:
+        raise CryptoError("luks_close failed for %s" % name)
+
+def luks_add_key(device,
+                 new_passphrase=None, new_key_file=None,
+                 passphrase=None, key_file=None):
+    p = os.pipe()
+    if passphrase:
+        os.write(p[1], "%s\n" % passphrase)
+        key_spec = ""
+    elif key_file and os.path.isfile(key_file):
+        key_spec = "--key-file %s" % key_file
+    else:
+        raise ValueError("luks_add_key requires either a passphrase or a key file")
+
+    if new_passphrase:
+        os.write(p[1], "%s\n" % new_passphrase)
+        new_key_spec = ""
+    elif new_key_file and os.path.isfile(new_key_file):
+        new_key_spec = "%s" % new_key_file
+    else:
+        raise ValueError("luks_add_key requires either a passphrase or a key file to add")
+
+    os.close(p[1])
+
+    rc = iutil.execWithRedirect("cryptsetup",
+                                ["-q",
+                                 key_spec,
+                                 "luksAddKey",
+                                 device,
+                                 new_key_spec],
+                                stdin = p[0],
+                                stdout = "/dev/null",
+                                stderr = "/dev/null",
+                                searchPath = 1)
+
+    os.close(p[0])
+    if rc:
+        raise CryptoError("luks add key failed")
+
+def luks_remove_key(device,
+                    del_passphrase=None, del_key_file=None,
+                    passphrase=None, key_file=None):
+    p = os.pipe()
+    if passphrase:
+        os.write(p[1], "%s\n" % passphrase)
+        key_spec = ""
+    elif key_file and os.path.isfile(key_file):
+        key_spec = "--key-file %s" % key_file
+    else:
+        raise ValueError("luks_remove_key requires either a passphrase or a key file")
+
+    if del_passphrase:
+        os.write(p[1], "%s\n" % del_passphrase)
+        del_key_spec = ""
+    elif del_key_file and os.path.isfile(del_key_file):
+        del_key_spec = "%s" % del_key_file
+    else:
+        raise ValueError("luks_remove_key requires either a passphrase or a key file to remove")
+
+    os.close(p[1])
+
+    rc = iutil.execWithRedirect("cryptsetup",
+                                ["-q",
+                                 key_spec,
+                                 "luksRemoveKey",
+                                 device,
+                                 del_key_spec],
+                                stdin = p[0],
+                                stdout = "/dev/null",
+                                stderr = "/dev/null",
+                                searchPath = 1)
+
+    os.close(p[0])
+    if rc:
+        raise CryptoError("luks_remove_key failed")
+
+
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/storage/devicelibs/dm.py anaconda-storage/storage/devicelibs/dm.py
--- anaconda/storage/devicelibs/dm.py	1969-12-31 18:00:00.000000000 -0600
+++ anaconda-storage/storage/devicelibs/dm.py	2009-02-12 16:17:59.000000000 -0600
@@ -0,0 +1,77 @@
+import os
+import iutil
+
+import gettext
+_ = lambda x: gettext.ldgettext("anaconda", x)
+
+import logging
+log = logging.getLogger("storage")
+
+class DMError(Exception):
+    pass
+
+def name_from_dm_node(dm_node):
+    st = os.stat("/dev/%s" % dm_node)
+    major = os.major(st.st_rdev)
+    minor = os.minor(st.st_rdev)
+    name = iutil.execWithCapture("dmsetup",
+                                 ["info", "--columns",
+                                  "--noheadings", "-o", "name",
+                                  "-j", str(major), "-m", str(minor)],
+                                 stderr="/dev/null")
+    log.debug("name_from_dm(%s) returning '%s'" % (dm_node, name.strip()))
+    return name.strip()
+
+def dm_node_from_name(map_name):
+    devnum = iutil.execWithCapture("dmsetup",
+                                   ["info", "--columns",
+                                    "--noheadings",
+                                    "-o", "devno",
+                                    map_name],
+                                    stderr="/dev/null")
+    (major, sep, minor) = devnum.strip().partition(":")
+    if not sep:
+        raise DMError("dm device does not exist")
+
+    dm_node = "dm-%d" % int(minor)
+    log.debug("dm_node_from_name(%s) returning '%s'" % (map_name, dm_node))
+    return dm_node
+
+def _get_backing_devnums_from_map(map_name):
+    ret = []
+    buf = iutil.execWithCapture("dmsetup",
+                                ["info", "--columns",
+                                 "--noheadings",
+                                 "-o", "devnos_used",
+                                 map_name],
+                                stderr="/dev/null")
+    dev_nums = buf.split()
+    for dev_num in dev_nums:
+        (major, colon, minor) = dev_num.partition(":")
+        ret.append((int(major), int(minor)))
+
+    return ret
+
+def get_backing_devnums(dm_node):
+    #dm_node = dm_node_from_name(map_name)
+    if not dm_node:
+        return None
+
+    top_dir = "/sys/block"
+    backing_devs = os.listdir("%s/%s/slaves/" % (top_dir, dm_node))
+    dev_nums = []
+    for backing_dev in backing_devs:
+        dev_num = open("%s/%s/dev" % (top_dir, backing_dev)).read().strip()
+        (_major, _minor) = dev_num.split(":")
+        dev_nums.append((int(_major), int(_minor)))
+
+    return dev_nums
+
+def get_backing_devs_from_name(map_name):
+    dm_node = dm_node_from_name(map_name)
+    if not dm_node:
+        return None
+
+    slave_devs = os.listdir("/sys/block/virtual/%s" % dm_node)
+    return slave_devs
+
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/storage/devicelibs/lvm.py anaconda-storage/storage/devicelibs/lvm.py
--- anaconda/storage/devicelibs/lvm.py	1969-12-31 18:00:00.000000000 -0600
+++ anaconda-storage/storage/devicelibs/lvm.py	2009-02-16 18:38:41.000000000 -0600
@@ -0,0 +1,330 @@
+
+import math
+
+import iutil
+import gettext
+_ = lambda x: gettext.ldgettext("anaconda", x)
+
+class LVMError(Exception):
+    pass
+
+def getPossiblePhysicalExtents(floor=0):
+    """Returns a list of integers representing the possible values for
+       the physical extent of a volume group.  Value is in KB.
+
+       floor - size (in KB) of smallest PE we care about.
+    """
+
+    possiblePE = []
+    curpe = 8
+    while curpe <= 16384*1024:
+	if curpe >= floor:
+	    possiblePE.append(curpe)
+	curpe = curpe * 2
+
+    return possiblePE
+
+def getMaxLVSize(pe):
+    """Given a PE size in KB, returns maximum size (in MB) of a logical volume.
+
+    pe - PE size in KB
+    """
+    if iutil.getArch() in ("x86_64", "ppc64"): #64bit architectures
+        return (8*1024*1024*1024*1024) #Max is 8EiB (very large number..)
+    else:
+        return (16*1024*1024) #Max is 16TiB
+
+def safeLvmName(str):
+    tmp = string.strip(str)
+    tmp = tmp.replace("/", "_")
+    tmp = re.sub("[^0-9a-zA-Z._]", "", str)
+    tmp = tmp.lstrip("_")
+
+    return tmp
+
+def createSuggestedVGName(partitions, network):
+    """Given list of partition requests, come up with a reasonable VG name
+
+    partitions - list of requests
+    """
+
+    # try to create a volume group name incorporating the hostname
+    hn = network.hostname
+    if hn is not None and hn != '':
+        if hn == 'localhost' or hn == 'localhost.localdomain':
+            vgtemplate = "VolGroup"
+        elif hn.find('.') != -1:
+            hn = safeLvmName(hn)
+            vgtemplate = "vg_%s" % (hn.split('.')[0].lower(),)
+        else:
+            hn = safeLvmName(hn)
+            vgtemplate = "vg_%s" % (hn.lower(),)
+    else:
+        vgtemplate = "VolGroup"
+
+    if not partitions.isVolumeGroupNameInUse(vgtemplate):
+        return vgtemplate
+    else:
+        i = 0
+        while 1:
+            tmpname = "%s%02d" % (vgtemplate, i,)
+            if not partitions.isVolumeGroupNameInUse(tmpname):
+                break
+
+            i += 1
+            if i > 99:
+                tmpname = ""
+
+        return tmpname
+
+def createSuggestedLVName(logreqs):
+    """Given list of LV requests, come up with a reasonable LV name
+
+    partitions - list of LV requests for this VG
+    """
+
+    i = 0
+
+    lnames = []
+    for lv in logreqs:
+        lnames.append(lv.logicalVolumeName)
+
+    while 1:
+        tmpname = "LogVol%02d" % (i,)
+        if (logreqs is None) or (tmpname not in lnames):
+            break
+
+        i += 1
+        if i > 99:
+            tmpname = ""
+
+    return tmpname
+
+def getVGUsedSpace(vgreq, requests, diskset):
+    vgused = 0
+    for request in requests.requests:
+	if request.type == REQUEST_LV and request.volumeGroup == vgreq.uniqueID:
+	    size = int(request.getActualSize(requests, diskset))
+	    vgused = vgused + size
+
+
+    return vgused
+
+def getVGFreeSpace(vgreq, requests, diskset):
+    raise NotImplementedError
+    used = getVGUsedSpace(vgreq, requests, diskset)
+    log.debug("used space is %s" % (used,))
+    
+    total = vgreq.getActualSize(requests, diskset)
+    log.debug("actual space is %s" % (total,))
+    return total - used
+
+def clampSize(size, pesize, roundup=None):
+    if roundup:
+        round = math.ceil
+    else:
+        round = math.floor
+
+    return long(round(float(size)/float(pesize)) * pesize)
+
+def pvcreate(device):
+    rc = iutil.execWithRedirect("lvm",
+                                ["pvcreate", device],
+                                stdout = "/dev/null",
+                                stderr = "/dev/null",
+                                searchPath=1)
+    if rc:
+        raise LVMError("pvcreate failed for %s" % device)
+
+def pvresize(device, size):
+    size_arg = "%dm" % size
+    rc = iutil.execWithRedirect("lvm",
+                                ["pvresize",
+                                 "--setphysicalvolumesize", size_arg,
+                                 device],
+                                stdout = "/dev/null",
+                                stderr = "/dev/null",
+                                searchPath=1)
+    if rc:
+        raise LVMError("pvresize failed for %s" % device)
+
+def pvremove(device):
+    rc = iutil.execWithRedirect("lvm",
+                                ["pvremove", device],
+                                stdout = "/dev/null",
+                                stderr = "/dev/null",
+                                searchPath=1)
+    if rc:
+        raise LVMError("pvremove failed for %s" % device)
+
+def pvinfo(device):
+    """
+        If the PV was created with '--metadacopies 0', lvm will do some
+        scanning of devices to determine from their metadata which VG
+        this PV belongs to.
+
+        pvs -o pv_name,pv_mda_count,vg_name,vg_uuid --config \
+            'devices { scan = "/dev" filter = ["a/loop0/", "r/.*/"] }'
+    """
+    #cfg = "'devices { scan = \"/dev\" filter = [\"a/%s/\", \"r/.*/\"] }'" 
+    rc = iutil.execWithCapture("lvm",
+                               ["pvs", "--noheadings",
+                                "--units", "m",
+                                "-o",
+                                "pv_name,pv_mda_count,vg_name,vg_uuid",
+                                device],
+                                stderr = "/dev/null")
+    vals = rc.split()
+    if not vals:
+        raise LVMError("pvinfo failed for %s" % device)
+
+    info = {'pv_name': vals[0],
+            'vg_name': vals[2],
+            'vg_uuid': vals[3]}
+    return info
+
+def vgcreate(vg_name, pvs, pe_size):
+    argv = ["vgcreate"]
+    if pe_size:
+        argv.extend(["-s", "%dM" % pe_size])
+    pv_list = " ".join(pvs)
+    argv.append(vg_name)
+    argv.append(pv_list)
+    rc = iutil.execWithRedirect("lvm",
+                                argv,
+                                stdout = "/dev/null",
+                                stderr = "/dev/null",
+                                searchPath=1)
+
+    if rc:
+        raise LVMError("vgcreate failed for %s" % vg_name)
+
+def vgremove(vg_name):
+    rc = iutil.execWithRedirect("lvm", ["vgremove", vg_name],
+                                stdout = "/dev/null",
+                                stderr = "/dev/null",
+                                searchPath=1)
+
+    if rc:
+        raise LVMError("vgremove failed for %s" % vg_name)
+
+def vgactivate(vg_name):
+    rc = iutil.execWithRedirect("lvm", ["vgchange" "-a", "y", vg_name],
+                                stdout = "/dev/null",
+                                stderr = "/dev/null",
+                                searchPath=1)
+    if rc:
+        raise LVMError("vgactivate failed for %s" % vg_name)
+
+def vgdeactivate(vg_name):
+    rc = iutil.execWithRedirect("lvm", ["vgchange", "-a", "n", vg_name],
+                                stdout = "/dev/null",
+                                stderr = "/dev/null",
+                                searchPath=1)
+
+    if rc:
+        raise LVMError("vgdeactivate failed for %s" % vg_name)
+
+def vgreduce(vg_name, pv_list):
+    pvs = " ".join(pv_list)
+    rc = iutil.execWithRedirect("lvm", ["vgreduce", vg_name, pvs],
+                                stdout = "/dev/null",
+                                stderr = "/dev/null",
+                                searchPath=1)
+
+    if rc:
+        raise LVMError("vgreduce failed for %s" % vg_name)
+
+def vginfo(vg_name):
+    buf = iutil.execWithCapture("lvm",
+                ["vgs", "--noheadings", "--nosuffix", "--units", "m", "-o", 
+                 "uuid,size,free,extent_size,extent_count,free_count,pv_count",
+                 vg_name],
+                 stderr="/dev/null")
+    info = buf.split()
+    if len(info) != 7:
+        raise LVMError(_("vginfo failed for %s" % vg_name))
+
+    d = {}
+    (d['uuid'],d['size'],d['free'],d['pe_size'],
+     d['pe_count'],d['pe_free'],d['pv_count']) = info
+    return d
+
+def lvs(vg_name):
+    buf = iutil.execWithCapture("lvm",
+                                ["lvs", "--noheadings", "--nosuffix",
+                                 "--units", "m", "-o",
+                                 "lv_name,lv_uuid,lv_size"],
+                                stderr="/dev/null")
+
+
+    lvs = {}
+    for line in buf.splitlines():
+        line = line.strip()
+        if not line:
+            continue
+        (name, uuid, size) = line.split()
+        lvs[name] = {"size": size,
+                     "uuid": uuid}
+    return lvs
+
+def lvcreate(vg_name, lv_name, size):
+    size_arg = "%dm" % size
+    rc = iutil.execWithRedirect("lvm",
+                                ["lvcreate",
+                                 "-L", size_arg,
+                                 "-n", lv_name,
+                                 vg_name],
+                                stdout = "/dev/null",
+                                stderr = "/dev/null",
+                                searchPath=1)
+
+    if rc:
+        raise LVMError("lvcreate failed for %s/%s" % (vg_name, lv_name))
+
+def lvremove(vg_name, lv_name):
+    lv_path = "%s/%s" % (vg_name, lv_name)
+    rc = iutil.execWithRedirect("lvm", ["lvremove", lv_path],
+                                stdout = "/dev/null",
+                                stderr = "/dev/null",
+                                searchPath=1)
+
+    if rc:
+        raise LVMError("lvremove failed for %s" % lv_path)
+
+def lvresize(vg_name, lv_name, size):
+    lv_path = "%s/%s" % (vg_name, lv_name)
+    size_arg = "%dm" % size
+    rc = iutil.execWithRedirect("lvm",
+                                ["lvresize",
+                                 "-L", size_arg,
+                                 lv_path],
+                                stdout = "/dev/null",
+                                stderr = "/dev/null",
+                                searchPath=1)
+
+    if rc:
+        raise LVMError("lvresize failed for %s" % lv_path)
+
+def lvactivate(vg_name, lv_name):
+    # see if lvchange accepts paths of the form 'mapper/$vg-$lv'
+    lv_path = "%s/%s" % (vg_name, lv_name)
+    rc = iutil.execWithRedirect("lvm", ["lvchange", "-a", "y", lv_path],
+                                stdout = "/dev/null",
+                                stderr = "/dev/null",
+                                searchPath=1)
+    if rc:
+        raise LVMError("lvactivate failed for %s" % lv_path)
+
+def lvdeactivate(vg_name, lv_name):
+    lv_path = "%s/%s" % (vg_name, lv_name)
+    rc = iutil.execWithRedirect("lvm", ["lvchange", "-a", "n", lv_path],
+                                stdout = "/dev/null",
+                                stderr = "/dev/null",
+                                searchPath=1)
+
+    if rc:
+        raise LVMError("lvdeactivate failed for %s" % lv_path)
+
+
+
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/storage/devicelibs/mdraid.py anaconda-storage/storage/devicelibs/mdraid.py
--- anaconda/storage/devicelibs/mdraid.py	1969-12-31 18:00:00.000000000 -0600
+++ anaconda-storage/storage/devicelibs/mdraid.py	2009-02-16 16:43:11.000000000 -0600
@@ -0,0 +1,224 @@
+import iutil
+import os
+import gettext
+_ = lambda x: gettext.ldgettext("anaconda", x)
+
+class MDRaidError(Exception):
+    pass
+
+def getRaidLevels():
+    avail = []
+    try:
+        f = open("/proc/mdstat", "r")
+    except:
+        pass
+    else:
+        for l in f.readlines():
+            if not l.startswith("Personalities"):
+                continue
+
+            lst = l.split()
+
+            for lev in ["RAID0", "RAID1", "RAID5", "RAID6", "RAID10"]:
+                if "[" + lev + "]" in lst or "[" + lev.lower() + "]" in lst:
+                    avail.append(lev)
+
+        f.close()
+
+    avail.sort()
+    return avail
+
+raid_levels = getRaidLevels()
+
+def isRaid10(raidlevel):
+    """Return whether raidlevel is a valid descriptor of RAID10."""
+    return raidlevel in ("RAID10", "10", 10)
+
+def isRaid6(raidlevel):
+    """Return whether raidlevel is a valid descriptor of RAID6."""
+    return raidlevel in ("RAID6", "6", 6)
+
+def isRaid5(raidlevel):
+    """Return whether raidlevel is a valid descriptor of RAID5."""
+    return raidlevel in ("RAID5", "5", 5)
+
+def isRaid1(raidlevel):
+    """Return whether raidlevel is a valid descriptor of RAID1."""
+    return raidlevel in ("mirror", "RAID1", "1", 1)
+
+def isRaid0(raidlevel):
+    """Return whether raidlevel is a valid descriptor of RAID0."""
+    return raidlevel in ("stripe", "RAID0", "0", 0)
+
+def get_raid_min_members(raidlevel):
+    """Return the minimum number of raid members required for raid level"""
+    if isRaid0(raidlevel):
+        return 2
+    elif isRaid1(raidlevel):
+        return 2
+    elif isRaid5(raidlevel):
+        return 3
+    elif isRaid6(raidlevel):
+        return 4
+    elif isRaid10(raidlevel):
+        return 4
+    else:
+        raise ValueError, "invalid raidlevel in get_raid_min_members"
+
+def get_raid_max_spares(raidlevel, nummembers):
+    """Return the maximum number of raid spares for raidlevel."""
+    if isRaid0(raidlevel):
+        return 0
+    elif isRaid1(raidlevel) or isRaid5(raidlevel) or \
+         isRaid6(raidlevel) or isRaid10(raidlevel):
+        return max(0, nummembers - get_raid_min_members(raidlevel))
+    else:
+        raise ValueError, "invalid raidlevel in get_raid_max_spares"
+
+def mdcreate(device, level, disks, spares=0):
+    argv = ["--create", device, "--level", str(level)]
+    raid_devs = len(disks) - spares
+    argv.append("--raid-devices=%d" % raid_devs)
+    if spares:
+        argv.append("--spare-devices=%d" % spares)
+    argv.extend(disks)
+    
+    rc = iutil.execWithRedirect("mdadm",
+                                argv,
+                                stderr = "/dev/null",
+                                stdout = "/dev/null",
+                                searchPath=1)
+
+    if rc:
+        raise MDRaidError("mdcreate failed for %s" % device)
+
+    # mdadm insists on starting the new array, so we have to stop it here
+    #self.mddeactivate(device)
+
+def mddestroy(device):
+    rc = iutil.execWithRedirect("mdadm",
+                                ["--zero-superblock", device],
+                                stderr = "/dev/null",
+                                stdout = "/dev/null",
+                                searchPath=1)
+
+    if rc:
+        raise MDRaidError("mddestroy failed for %s" % device)
+
+def mdadd(device):
+    # XXX NOTUSED: mdadm -I is broken and dledford says it should be
+    #              avoided if possible, so we used mdadm -A instead
+    rc = iutil.execWithRedirect("mdadm",
+                                ["--incremental", 
+                                 "--quiet",
+                                 "--auto=yes",
+                                 device],
+                                stderr = "/dev/null",
+                                stdout = "/dev/null",
+                                searchPath=1)
+
+    if rc:
+        raise MDRaidError("mdadd failed for %s" % device)
+
+def mdactivate(device, members=[], super_minor=None, uuid=None):
+    if super_minor is None and not uuid:
+        raise ValueError("mdactivate requires either a uuid or a super-minor")
+    
+    if uuid:
+        identifier = "--uuid=%s" % uuid
+    elif super_minor is not None:
+        identifier = "--super-minor=%d" % super_minor
+    else:
+        identifier = ""
+
+    filename = None
+    if members:
+        from tempfile import mkstemp
+        (fd, filename) = mkstemp(prefix="%s_devices." % device,
+                                     dir="/tmp",
+                                     text=True)
+        os.write(fd, "DEVICE %s\n" % " ".join(members))
+        config_arg = "--config=%s" % filename
+        os.close(fd)
+        del mkstemp
+    else:
+        config_arg = ""
+
+    rc = iutil.execWithRedirect("mdadm",
+                                ["--assemble",
+                                 config_arg,
+                                 device,
+                                 identifier,
+                                 "--auto=md",
+                                 "--update=super-minor"],
+                                stderr = "/dev/null",
+                                stdout = "/dev/null",
+                                searchPath=1)
+
+    if filename and os.access(filename, os.R_OK):
+        try:
+            os.unlink(filename)
+        except OSError, e:
+            log.debug("unlink of %s failed: %s" % (filename, e))
+
+    if rc:
+        raise MDRaidError("mdactivate failed for %s" % device)
+
+
+def mddeactivate(device):
+    rc = iutil.execWithRedirect("mdadm",
+                                ["--stop", device],
+                                stderr = "/dev/null",
+                                stdout = "/dev/null",
+                                searchPath=1)
+
+    if rc:
+        raise MDRaidError("mddeactivate failed for %s" % device)
+
+def mdexamine(device):
+    # XXX NOTUSED: we grab metadata from udev, which ran 'mdadm -E --export'
+    #
+    # FIXME: this will not work with version >= 1 metadata
+    #
+    # We should use mdadm -Eb or mdadm -E --export for a more easily
+    # parsed output format.
+    lines = iutil.execWithCapture("mdadm",
+                                  ["--examine", device],
+                                  stderr="/dev/null").splitlines()
+
+    info = {
+            'major': "-1",
+            'minor': "-1",
+            'uuid' : "",
+            'level': -1,
+            'nrDisks': -1,
+            'totalDisks': -1,
+            'mdMinor': -1,
+        }
+
+    for line in lines:
+        (key, sep, val) = line.strip().partition(" : ")
+        if not sep:
+            continue
+        if key == "Version":
+            (major, sep, minor) = val.partition(".")
+            info['major'] = major
+            info['minor'] = minor
+        elif key == "UUID":
+            info['uuid'] = val.split()[0]
+        elif key == "Raid Level":
+            info['level'] = int(val[4:])
+        elif key == "Raid Devices":
+            info['nrDisks'] = int(val)
+        elif key == "Total Devices":
+            info['totalDisks'] = int(val)
+        elif key == "Preferred Minor":
+            info['mdMinor'] = int(val)
+        else:
+            continue
+
+    if not info['uuid']:
+        raise MDRaidError("UUID missing from device info")
+
+    return info
+
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/storage/devicelibs/swap.py anaconda-storage/storage/devicelibs/swap.py
--- anaconda/storage/devicelibs/swap.py	1969-12-31 18:00:00.000000000 -0600
+++ anaconda-storage/storage/devicelibs/swap.py	2009-02-12 18:59:46.000000000 -0600
@@ -0,0 +1,103 @@
+# swap.py
+# Python module for managing swap devices.
+#
+# Copyright (C) 2009  Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
+# Public License for more details.  You should have received a copy of the
+# GNU General Public License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.  Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): Dave Lehman <dlehman@redhat.com>
+#
+import iutil
+import gettext
+_ = lambda x: gettext.ldgettext("anaconda", x)
+
+from errors import *
+
+def mkswap(device, label=''):
+    argv = []
+    if label:
+        argv.extend(["-L", label])
+    argv.append(device)
+
+    rc = iutil.execWithRedirect("mkswap", argv,
+                                stderr = "/dev/null",
+                                stdout = "/dev/null",
+                                searchPath=1)
+
+    if rc:
+        raise SwapError("mkswap failed for '%s'" % device)
+
+def swapon(device, priority=None):
+    pagesize = resource.getpagesize()
+    buf = None
+    if pagesize > 2048:
+        num = pagesize
+    else:
+        num = 2048
+    try:
+        fd = os.open(device, os.O_RDONLY)
+        buf = os.read(fd, num)
+    except:
+        pass
+    finally:
+        try:
+            os.close(fd)
+        except:
+            pass
+
+    if buf is not None and len(buf) == pagesize:
+        sig = buf[pagesize - 10:]
+        if sig == 'SWAP-SPACE':
+            raise OldSwapError
+        if sig == 'S1SUSPEND\x00' or sig == 'S2SUSPEND\x00':
+            raise SuspendError
+
+    argv = []
+    if 0 <= priority <= 32767:
+        argv.extend(["-p", priority])
+    argv.append(device)
+        
+    rc = iutil.execWithRedirect("swapon",
+                                argv,
+                                stderr = "/dev/null",
+                                stdout = "/dev/null",
+                                searchPath=1)
+
+    if rc:
+        raise SwapError("swapon failed for '%s'" % device)
+
+def swapoff(device):
+    rc = iutil.execWithRedirect("swapoff", [device],
+                                stderr = "/dev/null",
+                                stdout = "/dev/null",
+                                searchPath=1)
+
+    if rc:
+        raise SwapError("swapoff failed for '%s'" % device)
+
+def swapstatus(device):
+    lines = open("/proc/swaps").readlines()
+    status = False
+    for line in lines:
+        if not line.strip():
+            continue
+            
+        if line.split()[0] == device:
+            status = True
+            break
+
+    return status
+
+
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/storage/devices.py anaconda-storage/storage/devices.py
--- anaconda/storage/devices.py	1969-12-31 18:00:00.000000000 -0600
+++ anaconda-storage/storage/devices.py	2009-02-17 10:16:05.000000000 -0600
@@ -0,0 +1,2373 @@
+# devices.py
+# Device classes for anaconda's storage configuration module.
+# 
+# Copyright (C) 2009  Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
+# Public License for more details.  You should have received a copy of the
+# GNU General Public License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.  Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): Dave Lehman <dlehman@redhat.com>
+#
+
+
+"""
+    Device classes for use by anaconda.
+
+    This is the hierarchy of device objects that anaconda will use for
+    managing storage devices in the system. These classes will
+    individually make use of external support modules as needed to
+    perform operations specific to the type of device they represent.
+
+    TODO:
+        - see how to do network devices (NetworkManager may help)
+          - perhaps just a wrapper here
+        - document return values of all methods/functions
+        - find out what other kinds of wild and crazy devices we need to
+          represent here (iseries? xen? more mainframe? mac? ps?)
+            - PReP
+              - this is a prime candidate for a PseudoDevice
+            - DASD
+            - ZFCP
+            - XEN
+
+    What specifications do we allow?              new        existing
+        partitions                              
+            usage                                  +            +
+                filesystem, partition type are implicit
+            mountpoint                             +            +
+            size
+                exact                              +            -
+                range                              +            -
+                resize                             -            +
+            format                                 -            +
+            encryption                             +            +
+
+            disk                                                 
+                exact                              +            -
+                set                                +            -
+                    how will we specify this?
+                        partition w/ multiple parents cannot otherwise occur
+            primary                                +            -
+
+        mdraid sets
+            filesystem (*)                         +            +
+            mountpoint                             +            +
+            size?                                                
+            format                                 -            +
+            encryption                             +            +
+
+            level                                  +            ? 
+            device minor                           +            ? 
+            member devices                         +            ? 
+            spares                                 +            ? 
+            name?
+            bitmap? (boolean)                      +            -
+
+        volume groups
+            name                                   +            - 
+            member pvs                             +            +
+            pesize                                 +            ?
+
+        logical volumes
+            filesystem                             +            +
+            mountpoint                             +            +
+            size
+                exact                              +            ?
+            format                                 -            +
+            encryption                             +            +
+
+            name                                   +            ?
+            vgname                                 +            ?
+
+
+"""
+import os
+import math
+
+# device backend modules
+import mdraid
+import lvm
+#import block
+import dm
+
+# XXX temporary
+import sys
+sys.path.insert(0, "/root/pyparted/src")
+sys.path.insert(1, "/root/pyparted/src/.libs")
+sys.path.append("devicelibs")
+import parted
+
+from errors import *
+from iutil import log_method_call, notify_kernel, numeric_type
+from udev import udev_settle
+from deviceformat import get_device_format_class, getFormat
+
+import gettext
+_ = lambda x: gettext.ldgettext("anaconda", x)
+
+import logging
+log = logging.getLogger("storage")
+
+def get_device_majors():
+    majors = {}
+    for line in open("/proc/devices").readlines():
+        try:
+            (major, device) = line.split()
+        except ValueError:
+            continue
+        try:
+            majors[int(major)] = device
+        except ValueError:
+            continue
+    return majors
+device_majors = get_device_majors()
+
+class Device(object):
+    """ A generic device.
+
+        Device instances know which devices they depend upon (parents
+        attribute). They do not know which devices depend upon them, but
+        they do know whether or not they have any dependent devices
+        (isleaf attribute).
+
+        A Device's setup method should set up all parent devices as well
+        as the device itself. It should not run the resident format's
+        setup method.
+
+            Which Device types rely on their parents' formats being active?
+                DMCryptDevice
+
+        A Device's teardown method should accept the keyword argument
+        recursive, which takes a boolean value and indicates whether or
+        not to recursively close parent devices.
+
+        A Device's create method should create all parent devices as well
+        as the device itself. It should also run the Device's setup method
+        after creating the device. The create method should not create a
+        device's resident format.
+
+            Which device type rely on their parents' formats to be created
+            before they can be created/assembled?
+                VolumeGroup
+                DMCryptDevice
+
+        A Device's destroy method should destroy any resident format
+        before destroying the device itself.
+
+    """
+    _type = "generic device"
+    _packages = []
+
+    def __init__(self, name, parents=[], description=''):
+        """ Create a Device instance.
+
+            Arguments:
+
+                name -- the device name (generally a device node's basename)
+
+            Keyword Arguments:
+
+                parents -- a list of required Device instances
+                description -- a string describing the device
+
+        """
+        self._name = name
+        if not isinstance(parents, list):
+            raise ValueError("parents must be a list of Device instances")
+        self.parents = parents
+        self.kids = 0
+        self.description = description
+
+        for parent in self.parents:
+            parent.addChild()
+
+    def removeChild(self):
+        log_method_call(self, name=self.name, kids=self.kids)
+        self.kids -= 1
+
+    def addChild(self):
+        log_method_call(self, name=self.name, kids=self.kids)
+        self.kids += 1
+
+    def setup(self):
+        """ Open, or set up, a device. """
+        raise NotImplementedError("setup method not defined for Device")
+
+    def teardown(self, recursive=None):
+        """ Close, or tear down, a device. """
+        raise NotImplementedError("teardown method not defined for Device")
+
+    def create(self):
+        """ Create the device. """
+        raise NotImplementedError("create method not defined for Device")
+
+    def destroy(self):
+        """ Destroy the device. """
+        raise NotImplementedError("destroy method not defined for Device")
+
+    def setupParents(self):
+        """ Run setup method of all parent devices. """
+        for parent in self.parents:
+            parent.setup()
+
+    def teardownParents(self, recursive=None):
+        """ Run teardown method of all parent devices. """
+        for parent in self.parents:
+            parent.teardown(recursive=recursive)
+
+    def createParents(self):
+        """ Run create method of all parent devices. """
+        log.info("NOTE: recursive device creation disabled")
+        for parent in self.parents:
+            if not parent.exists:
+                raise DeviceError("parent device does not exist")
+            #parent.create()
+
+    def dependsOn(self, dep):
+        """ Return True if this device depends on dep. """
+        # XXX does a device depend on itself?
+        if dep in self.parents:
+            return True
+
+        for parent in self.parents:
+            if parent.dependsOn(dep):
+                return True
+
+        return False
+
+    @property
+    def status(self):
+        """ This device's status.
+
+            For now, this should return a boolean:
+                True    the device is open and ready for use
+                False   the device is not open
+        """
+        return False
+
+    @property
+    def name(self):
+        """ This device's name. """
+        return self._name
+
+    @property
+    def isleaf(self):
+        """ True if this device has no children. """
+        return self.kids == 0
+
+    @property
+    def typeDescription(self):
+        """ String describing the device type. """
+        return self._type
+
+    @property
+    def type(self):
+        """ Device type. """
+        return self._type
+
+    @property
+    def packages(self):
+        """ List of packages required to manage devices of this type.
+
+            This list includes the packages required by this device's
+            format type as well those required by all of its parent 
+            devices.
+        """
+        packages = self._packages
+        packages.extend(self.format.packages)
+        for parent in parents:
+            for package in parent.packages:
+                if package not in packages:
+                    packages.append(package)
+
+            for package in parent.format.packages:
+                if package not in packages:
+                    packages.append(package)
+
+        return packages
+
+
+class NetworkDevice(Device):
+    """ A network device """
+    _type = "network device"
+
+    def __init__(self, name, parents=[]):
+        """ Create a NetworkDevice instance.
+
+            Arguments:
+
+                name -- the device name (generally an interface name)
+
+            Keyword Arguments:
+
+                parents -- a list of required Device instances
+                description -- a string describing the device
+
+        """
+        Device.__init__(self, name, parents=parents)
+        self.active = False
+
+
+class StorageDevice(Device):
+    """ A generic storage device.
+
+        A fully qualified path to the device node can be obtained via the
+        path attribute, although it is not guaranteed to be useful, or
+        even present, unless the StorageDevice's setup method has been
+        run.
+
+        StorageDevice instances can optionally contain a filesystem,
+        represented by an FS instance. A StorageDevice's create method
+        should create a filesystem if one has been specified.
+    """
+    _type = "storage device"
+    devDir = "/dev"
+    sysfsBlockDir = "/class/block"
+
+    def __init__(self, device, format=None,
+                 size=None, major=None, minor=None,
+                 sysfsPath='', parents=[], exists=None):
+        """ Create a StorageDevice instance.
+
+            Arguments:
+
+                device -- the device name (generally a device node's basename)
+
+            Keyword Arguments:
+
+                size -- the device's size (units/format TBD)
+                major -- the device major
+                minor -- the device minor
+                sysfsPath -- sysfs device path
+                format -- a DeviceFormat instance
+                parents -- a list of required Device instances
+                description -- a string describing the device
+
+        """
+        # allow specification of individual parents
+        if isinstance(parents, Device):
+            parents = [parents]
+
+        Device.__init__(self, device, parents=parents)
+
+        self.uuid = None
+        self.format = None
+        self._size = numeric_type(size)
+        self.major = numeric_type(major)
+        self.minor = numeric_type(minor)
+        self.sysfsPath = sysfsPath
+        self.exists = exists
+
+        # this may be handy for disk, dmraid, mpath, mdraid
+        self.diskLabel = None
+
+        self.format = format
+        self.fstabComment = ""
+
+    @property
+    def path(self):
+        """ Device node representing this device. """
+        return "%s/%s" % (self.devDir, self.name)
+
+    def probe(self):
+        """ Probe for any missing information about this device. """
+        raise NotImplementedError("probe method not defined for StorageDevice")
+
+    def updateSysfsPath(self):
+        """ Update this device's sysfs path. """
+        log_method_call(self, self.name, status=self.status)
+        self.sysfsPath = os.path.join("/sys",
+                                      self.sysfsBlockDir,
+                                      self.name)
+        log.debug("%s sysfsPath set to %s" % (self.name, self.sysfsPath))
+
+    def notifyKernel(self):
+        """ Send a 'change' uevent to the kernel for this device. """
+        log_method_call(self, self.name, status=self.status)
+        if not self.exists:
+            log.debug("not sending change uevent for non-existent device")
+            return
+
+        if not self.status:
+            log.debug("not sending change uevent for inactive device")
+            return
+
+        self.updateSysfsPath()
+        path = os.path.normpath("/sys/%s" % self.sysfsPath)
+        try:
+            notify_kernel(path, action="change")
+        except Exception, e:
+            log.warning("failed to notify kernel of change: %s" % e)
+
+    @property
+    def fstabSpec(self):
+        spec = self.path
+        if self.format and self.format.uuid:
+            spec = "UUID=%s" % self.format.uuid
+        return spec
+
+    def resizeFormat(self):
+        """ Resize resident filesystems, &c. """
+        if not self.exists:
+            raise DeviceError("device has not been created")
+
+        if self.format:
+            self.format.resize()
+
+    def resize(self):
+        """ Resize the device.
+
+            New size should already be set.
+        """
+        raise NotImplementedError("resize method not defined for StorageDevice")
+
+    def setupFormat(self):
+        """ Setup filesystems, swap space, &c. """
+        log_method_call(self, self.name, status=self.status)
+        if not self.exists:
+            raise DeviceError("device has not been created")
+
+        if self.format:
+            try:
+                self.format.setup()
+            except Exception, e:
+                # this was already logged by the format' class
+                pass
+
+    def setup(self):
+        """ Open, or set up, a device. """
+        log_method_call(self, self.name, status=self.status)
+        if not self.exists:
+            raise DeviceError("device has not been created")
+
+        self.setupParents()
+
+    def teardownFormat(self):
+        """ Teardown filesystems, swap space, &c. """
+        log_method_call(self, self.name, status=self.status)
+        if not self.exists:
+            return
+        #    raise DeviceError("device has not been created")
+
+        if self.format:
+            self.format.teardown()
+
+    def teardown(self, recursive=None):
+        """ Close, or tear down, a device. """
+        log_method_call(self, self.name, status=self.status)
+        if not self.exists:
+            return
+        self.teardownFormat()
+        if recursive:
+            self.teardownParents(recursive=recursive)
+
+    def getSize(self):
+        """ Get the device's size. """
+        return self._size
+
+    def setSize(self, newsize):
+        """ Set the device's size to a new value. """
+        # FIXME: this should involve some verification
+        self._size = newsize
+
+    size = property(lambda x: x.getSize(), lambda x, y: x.setSize(y),
+                    doc="The device's size")
+
+    @property
+    def status(self):
+        """ This device's status.
+
+            For now, this should return a boolean:
+                True    the device is open and ready for use
+                False   the device is not open
+        """
+        if not self.exists:
+            return False
+        return os.access(self.path, os.W_OK)
+
+    def setFormat(self, format):
+        """ Set the Device's format. """
+        old_type = self.format.type
+        if not format:
+            format = getFormat(None, device=self.path)
+        log_method_call(self, self.name, type=format.type,
+                        current=self._format.type, status=self.status)
+        if self.format and self._format.status:
+            # FIXME: self.format.status doesn't mean much
+            raise DeviceError("cannot replace active format")
+
+        self._format = format
+
+    def getFormat(self):
+        return self._format
+
+    format = property(lambda d: d.getFormat(),
+                      lambda d,f: d.setFormat(f),
+                      doc="The device's formatting.")
+
+    def create(self):
+        """ Create the device. """
+        log_method_call(self, self.name, status=self.status)
+        if self.exists:
+            raise DeviceError("device has already been created")
+
+        self.createParents()
+        self.exists = True
+        self.setup()
+        # XXX Do we need to poke the kernel?
+
+    def destroy(self):
+        """ Destroy the device. """
+        log_method_call(self, self.name, status=self.status)
+        if not self.exists:
+            raise DeviceError("device has not been created")
+
+        self.destroyFormat()
+        self.exists = False
+        for parent in self.parents:
+            parent.removeChild()
+
+    def createFormat(self):
+        """ Create resident filesystems, swap space, &c. """
+        log_method_call(self, self.name, status=self.status)
+        if not self.exists:
+            raise DeviceError("device has not been created")
+
+        if self.format:
+            self.format.create()
+            self.notifyKernel()
+
+    def destroyFormat(self):
+        """ Destroy resident filesystems, swap space, &c. """
+        log_method_call(self, self.name, status=self.status)
+        if not self.exists:
+            raise DeviceError("device has not been created")
+
+        if self.format:
+            # XXX We could see if the device was active on entry and only
+            #     run teardown if we ran setup from here.
+            self.setup()
+            self.format.destroy()
+            self.notifyKernel()
+
+class DiskDevice(StorageDevice):
+    """ A disk """
+    _type = "disk"
+
+    def __init__(self, device, format=None,
+                 size=None, major=None, minor=None,
+                 sysfsPath='', parents=[]):
+        """ Create a DiskDevice instance.
+
+            Arguments:
+
+                device -- the device name (generally a device node's basename)
+
+            Keyword Arguments:
+
+                size -- the device's size (units/format TBD)
+                major -- the device major
+                minor -- the device minor
+                sysfsPath -- sysfs device path
+                format -- a DeviceFormat instance
+                parents -- a list of required Device instances
+                removable -- whether or not this is a removable device
+
+            DiskDevices always exist.
+        """
+        # XXX we should never be here w/o size, major, and minor
+        StorageDevice.__init__(self, device, format=format, size=size,
+                               major=major, minor=minor, exists=True,
+                               sysfsPath=sysfsPath, parents=parents)
+
+        self.partedDevice = None
+                   part.format.type in ("ext3", "ext2", "vfat"):
+        self.partedDisk = None
+        self.removable = False
+        if 'parted' in globals().keys():
+            log.debug("looking up parted Device: %s" % self.path)
+            self.partedDevice = parted.Device(path=self.path)
+            if not self.partedDevice:
+                raise DeviceError("cannot find parted device instance")
+            log.debug("creating parted Disk: %s" % self.path)
+            self.partedDisk = parted.Disk(device=self.partedDevice)
+            if not self.partedDisk:
+                raise DeviceError("failed to create parted Disk")
+
+            self.probe()
+
+    #def open(self):
+    #    raise DeviceError, "shit's busted."
+
+    @property
+    def size(self):
+        """ The disk's size in MB """
+        if not self._size:
+            self._size = self.partedDisk.device.getSize()
+        return self._size
+
+    def setPartedDisk(self, disk):
+        log_method_call(self, self.name, disk_path=disk.device.path)
+        self.partedDisk = disk
+
+    def removePartition(self, device):
+        partition = self.partedDisk.getPartitionByPath(device.path)
+        if partition:
+            self.partedDisk.removePartition(partition)
+
+    def probe(self):
+        """ Probe for any missing information about this device.
+
+            pyparted should be able to tell us anything we want to know.
+            size, disklabel type, maybe even partition layout
+        """
+        if  not 'parted' in globals().keys():
+            return
+
+        log_method_call(self, self.name, size=self.size, partedDevice=self.partedDevice)
+        if not self.size:
+            self.size = self.partedDevice.getSize()
+        if not self.diskLabel:
+            log.debug("setting %s diskLabel to %s" % (self.name,
+                                                      self.partedDisk.type))
+            self.diskLabel = self.partedDisk.type
+
+    def create(self):
+        """ Create the device. """
+        log_method_call(self, self.name, status=self.status)
+        self.createParents()
+        if 'parted' in globals().keys():
+            self.partedDisk.commit()
+        self.notifyKernel()
+        self.setup()
+
+    def destroy(self):
+        """ Destroy the device. """
+        log_method_call(self, self.name, status=self.status)
+        self.setup()
+        if 'parted' in globals().keys():
+            self.partedDisk.deleteAllPartitions()
+            # this is perhaps a separate operation (wiping the disklabel)
+            self.partedDisk.clobber()
+            self.partedDisk.commit()
+        self.notifyKernel()
+        self.destroyFormat()
+        for parent in self.parents:
+            parent.removeChild()
+
+    def setup(self):
+        """ Open, or set up, a device. """
+        log_method_call(self, self.name, status=self.status)
+        if not os.path.exists(self.path):
+            raise DeviceError("device does not exist")
+
+    def teardown(self, recursive=False):
+        """ Close, or tear down, a device. """
+        log_method_call(self, self.name, status=self.status)
+        if not self.exists:
+            raise DeviceError("device has not been created")
+
+
+class PartitionDevice(StorageDevice):
+    """ A disk partition.
+
+        On types and flags...
+
+        We don't need to deal with numerical partition types at all.
+
+        The only type we are concerned with is primary/logical/extended.
+
+        Usage specification is accomplished through the use of flags,
+        which we will set according to the partition's format.
+    """
+    _type = "partition"
+
+    def __init__(self, device, format=None,
+                 size=None, grow=False, maxsize=None,
+                 major=None, minor=None, bootable=None,
+                 sysfsPath='', parents=[], exists=None,
+                 partType=None, primary=False):
+        """ Create a PartitionDevice instance.
+
+            Arguments:
+
+                device -- the device name (generally a device node's basename)
+
+            Keyword Arguments:
+
+                exists -- indicates whether this is an existing device
+                format -- the device's format (DeviceFormat instance)
+
+                For existing partitions:
+
+                    parents -- the disk that contains this partition
+                    major -- the device major
+                    minor -- the device minor
+                    sysfsPath -- sysfs device path
+
+                For new partitions:
+
+                    partType -- primary,extended,&c (as parted constant)
+                    grow -- whether or not to grow the partition
+                    maxsize -- max size for growable partitions (in MB)
+                    size -- the device's size (in MB)
+                    bootable -- whether the partition is bootable
+                    parents -- a list of potential containing disks
+        """
+        self.req_disks = []
+        self.req_partType = None
+        self.req_primary = None
+        self.req_grow = None
+        self.req_bootable = None
+        self.req_size = 0
+        self.req_base_size = 0
+        self.req_max_size = 0
+
+        StorageDevice.__init__(self, device, format=format, size=size,
+                               major=major, minor=minor, exists=exists,
+                               sysfsPath=sysfsPath, parents=parents)
+
+        if not exists:
+            # this is a request, not a partition -- it has no parents
+            self.req_disks = self.parents[:]
+            for dev in self.parents:
+                dev.removeChild()
+            self.parents = []
+
+        # FIXME: Validate partType, but only if this is a new partition
+        #        Otherwise, overwrite it with the partition's type.
+        self._partType = None
+        self.partedFlags = {}
+        self._partedPartition = None
+
+        # FIXME: Validate size, but only if this is a new partition.
+        #        For existing partitions we will get the size from
+        #        parted.
+
+        if self.exists and 'parted' in globals().keys():
+            log.debug("looking up parted Partition: %s" % self.path)
+            #self.partedPartition = parted.getPartitionByName(self.path)
+            self._partedPartition = self.disk.partedDisk.getPartitionByPath(self.path)
+            if not self._partedPartition:
+                raise DeviceError("cannot find parted partition instance")
+
+            # collect information about the partition from parted
+            self.probe()
+        else:
+            # XXX It might be worthwhile to create a shit-simple
+            #     PartitionRequest class and pass one to this constructor
+            #     for new partitions.
+            self.req_name = name
+            self.req_partType = partType
+            self.req_primary = primary
+            self.req_max_size = numeric_type(maxsize)
+            self.req_grow = grow
+            self.req_bootable = bootable
+
+            # req_size may be manipulated in the course of partitioning
+            self.req_size = self._size
+
+            # req_base_size will always remain constant
+            self.req_base_size = self._size
+
+    @property
+    def partType(self):
+        """ Get the partition's type (as parted constant). """
+        if self.partedPartition:
+            return self.partedPartition.type
+        else:
+            if self.exists:
+                raise DeviceError("partition exists but has no partedPartition")
+            return self._partType
+
+    @property
+    def isExtended(self):
+        return self.partType & parted.PARTITION_EXTENDED
+
+    @property
+    def isLogical(self):
+        return self.partType & parted.PARTITION_LOGICAL
+
+    @property
+    def isPrimary(self):
+        return self.partType == parted.PARTITION_NORMAL
+
+    @property
+    def isProtected(self):
+        return self.partType & parted.PARTITION_PROTECTED
+
+    @property
+    def getPartedPartition(self):
+        return self._partedPartition
+        log.debug("PartitionDevice %s has %d parents (%s)" % (self.name,
+                                                              len(self.parents),                                                              self.parents))
+        ppart = None
+        if len(self.parents) == 1:
+            if self._partedPartition and \
+               self.disk.partedDisk == self._partedPartition.disk:
+                return self._partedPartition
+
+            log.debug("path is %s ; partitions is %s" % (self.path,
+                                                         [p.path for p in self.disk.partedDisk.partitions]))
+            pdisk = self.disk.partedDisk
+            ppart = pdisk.getPartitionByPath(self.path)
+            self._partedPartition = ppart
+
+        return ppart
+
+    def setPartedPartition(self, partition):
+        """ Set this PartitionDevice's parted Partition instance. """
+        log_method_call(self, self.name)
+        if partition is None:
+            path = None
+        elif isinstance(partition, parted.Partition):
+            path = partition.path
+        else:
+            raise ValueError("partition must be a parted.Partition instance")
+
+        log.debug("device %s new partedPartition %s has path %s" % (self.name,
+                                                                    partition,
+                                                                    path))
+        self._partedPartition = partition
+        self._name = partition.getDeviceNodeName()
+
+    partedPartition = property(lambda d: d.getPartedPartition(),
+                               lambda d,p: d.setPartedPartition(p))
+
+    def dependsOn(self, dep):
+        """ Return True if this device depends on dep. """
+        if dep.type == "partition" and dep.isExtended and self.isLogical:
+            return True
+
+        return Device.dependsOn(self, dep)
+
+    def setFormat(self, format):
+        """ Set the Device's format. """
+        log_method_call(self, self.name)
+        StorageDevice.setFormat(self, format)
+
+        # now, set the flags on our parted.Partition
+        #if format:
+        #    self.partedPartitions
+
+    def setBootable(self, bootable):
+        """ Set the bootable flag for this partition. """
+        if 'parted' not in globals().keys():
+            return
+
+        if self.partedPartition:
+            if isFlagAvailable(parted.PARTITION_BOOT):
+                if bootable:
+                    self.partedPartition.setFlag(parted.PARTITION_BOOT)
+                else:
+                    self.partedPartition.unsetFlag(parted.PARTITION_BOOT)
+            else:
+                raise DeviceError(_("boot flag not available for this "
+                                    "partition"))
+        else:
+            if self.partType != parted.PARTITION_NORMAL:
+                raise DeviceError(_("boot flag only available to primary "
+                                    "partitions"))
+            else:
+                self.bootable = bootable
+
+    def probe(self):
+        """ Probe for any missing information about this device.
+
+            size, partition type, flags
+        """
+        log_method_call(self, self.name, exists=self.exists)
+        if not self.exists:
+            return
+
+        # build a dict of this partition's parted flags
+        """
+        XXX removed temporarily to make things faster
+        for flag in parted.partitionFlag.keys():
+            if self.partedPartition.isFlagAvailable(flag):
+                self.partedFlags[flag] = self.partedPartition.getFlag(flag)
+
+        if self.partedFlags[parted.PARTITION_BOOT]:
+            self.bootable = True
+        else:
+            self.bootable = False
+        """
+
+        # this is in MB
+        self.size = self.partedPartition.getSize()
+
+        self._partType = self.partedPartition.type
+
+    def createFormat(self):
+        """ Create resident filesystems, swap space, &c. """
+        log_method_call(self, self.name, status=self.status)
+        if not self.exists:
+            raise DeviceError("device has not been created")
+
+        if self.format:
+            # partition flags should already have been set
+            self.format.create()
+            self.notifyKernel()
+
+    def create(self):
+        """ Create the device. """
+        log_method_call(self, self.name, status=self.status)
+        if self.exists:
+            raise DeviceError("device already exists")
+
+        # first, do the parted commit
+        try:
+            self.disk.create()
+        except DeviceError, e:
+            log.debug("error creating %s: %s" % (self.parents[0].name, e))
+
+        self.exists = True
+        self.setup()
+
+    def destroy(self):
+        """ Destroy the device. """
+        log_method_call(self, self.name, status=self.status)
+        if not self.exists:
+            raise DeviceError("device has not been created")
+
+        if not self.sysfsPath:
+            return
+
+        if not self.isleaf:
+            raise DeviceError("Cannot destroy non-leaf device")
+
+        self.destroyFormat()
+
+        if False:
+            # now actually tell pyparted to remove the partition
+            # FIXME: need special handling for extended partitions
+            parted.deletePartition(self.partedPartition)
+
+        self.notifyKernel()
+        self.exists = False
+        for parent in self.parents:
+            parent.removeChild()
+
+    def getSize(self):
+        """ Get the device's size. """
+        if self.partedPartition:
+            # this defaults to MB
+            return self.partedPartition.getSize()
+        else:
+            if self.exists:
+                raise DeviceError("partition exists but has no partedPartition")
+            return self._size
+
+    def setSize(self, newsize):
+        """ Set the device's size (for resize, not creation).
+
+            Arguments:
+
+                newsize -- the new size (in MB)
+
+        """
+        log_method_call(self, self.name,
+                        status=self.status, size=self._size, newsize=newsize)
+        if not self.exists:
+            raise DeviceError("device does not exist")
+
+        if newsize > self.disk.size:
+            raise ValueError("partition size would exceed disk size")
+
+        # this defaults to MB
+        maxAvailableSize = self.partedPartition.getMaxAvailableSize()
+
+        if newsize > maxAvailableSize:
+            raise ValueError("new size is greater than available space")
+
+         # now convert the size to sectors and update the geometry
+        geometry = self.partedPartition.geometry
+        physicalSectorSize = geometry.device.physicalSectorSize
+
+        new_length = (newsize * (1024 * 1024)) / physicalSectorSize
+        geometry.length = new_length
+
+    def getDisk(self):
+        """ The disk that contains this partition. """
+        try:
+            disk = self.parents[0]
+        except IndexError:
+            disk = None
+        return disk
+
+    def setDisk(self, disk):
+        if self.disk:
+            self.disk.removeChild()
+
+        self.parents = [disk]
+        disk.addChild()
+
+    disk = property(lambda p: p.getDisk(), lambda p,d: p.setDisk(d))
+
+
+class DMDevice(StorageDevice):
+    """ A device-mapper device """
+    _type = "dm"
+    devDir = "/dev/mapper"
+
+    def __init__(self, name, format=None, size=None, dmUuid=None,
+                 target=None, exists=None, parents=[], sysfsPath=''):
+        """ Create a DMDevice instance.
+
+            Arguments:
+
+                name -- the device name (generally a device node's basename)
+
+            Keyword Arguments:
+
+                target -- the device-mapper target type (string)
+                size -- the device's size (units/format TBD)
+                dmUuid -- the device's device-mapper UUID
+                sysfsPath -- sysfs device path
+                format -- a DeviceFormat instance
+                parents -- a list of required Device instances
+                exists -- indicates whether this is an existing device
+        """
+        StorageDevice.__init__(self, name, format=format, size=size,
+                               exists=exists,
+                               parents=parents, sysfsPath=sysfsPath)
+        self.target = target
+        self.dmUuid = dmUuid
+
+    def probe(self):
+        """ Probe for any missing information about this device.
+
+            target type, parents?
+        """
+        raise NotImplementedError("probe method not defined for DMDevice")
+
+    @property
+    def fstabSpec(self):
+        """ Return the device specifier for use in /etc/fstab. """
+        return self.path
+
+    def updateSysfsPath(self):
+        """ Update this device's sysfs path. """
+        log_method_call(self, self.name, status=self.status)
+        if not self.exists:
+            raise DeviceError("device has not been created")
+
+        if self.status:
+            dm_node = self.getDMNode()
+            self.sysfsPath = os.path.join("/sys", self.sysfsBlockDir, dm_node)
+        else:
+            self.sysfsPath = None
+
+    #def getTargetType(self):
+    #    return dm.getDmTarget(name=self.name)
+
+    def getDMNode(self):
+        """ Return the dm-X (eg: dm-0) device node for this device. """
+        log_method_call(self, self.name, status=self.status)
+        if not self.exists:
+            raise DeviceError("device has not been created")
+
+        return dm.dm_node_from_name(self.name)
+        #return block.getDmNodeFromName(self.name)
+
+    def setName(self, name):
+        """ Set the device's map name. """
+        log_method_call(self, self.name, status=self.status)
+        if self.status:
+            raise DeviceError("device is active")
+
+        self._name = name
+        #self.sysfsPath = "/dev/disk/by-id/dm-name-%s" % self.name
+
+    def teardown(self, recursive=None):
+        """ Close, or tear down, a device. """
+        log_method_call(self, self.name, status=self.status)
+        if not self.exists:
+            raise DeviceError("device has not been created")
+
+        if self.status:
+            self.teardownFormat()
+
+        if recursive:
+            self.teardownParents(recursive=recursive)
+
+
+class DMCryptDevice(DMDevice):
+    """ A dm-crypt device """
+    _type = "dm-crypt"
+
+    def __init__(self, name, format=None, size=None, uuid=None,
+                 exists=None, sysfsPath='', parents=[]):
+        """ Create a DMCryptDevice instance.
+
+            Arguments:
+
+                name -- the device name (generally a device node's basename)
+
+            Keyword Arguments:
+
+                size -- the device's size (units/format TBD)
+                sysfsPath -- sysfs device path
+                format -- a DeviceFormat instance
+                parents -- a list of required Device instances
+                exists -- indicates whether this is an existing device
+        """
+        DMDevice.__init__(self, name, format=format, size=size,
+                          parents=parents, sysfsPath=sysfsPath,
+                          exists=exists, target="crypt")
+
+class LUKSDevice(DMCryptDevice):
+    """ A mapped LUKS device. """
+    _type = "luks/dm-crypt"
+
+    def __init__(self, name, format=None, size=None, uuid=None,
+                 exists=None, sysfsPath='', parents=[]):
+        """ Create a LUKSDevice instance.
+
+            Arguments:
+
+                name -- the device name
+
+            Keyword Arguments:
+
+                size -- the device's size in MB
+                uuid -- the device's UUID
+                sysfsPath -- sysfs device path
+                format -- a DeviceFormat instance
+                parents -- a list of required Device instances
+                exists -- indicates whether this is an existing device
+        """
+        DMCryptDevice.__init__(self, name, format=format, size=size,
+                               parents=parents, sysfsPath=sysfsPath,
+                               uuid=None, exists=exists)
+
+    def create(self):
+        """ Create the device. """
+        log_method_call(self, self.name, status=self.status)
+        if self.exists:
+            raise DeviceError("device already exists")
+
+        self.createParents()
+
+        # This is unusual in that we need the parent device's format to
+        # be created before we can create our device.
+        self.slave.createFormat()
+        self.exists = True
+        self.setup()
+
+    def setup(self):
+        """ Open, or set up, a device. """
+        log_method_call(self, self.name, status=self.status)
+        if not self.exists:
+            raise DeviceError("device has not been created")
+
+        self.slave.setup()
+
+        # This one's a little unusual in that we need the parent device's
+        # format to be active before we can be set up ourselves.
+        self.slave.setupFormat()
+
+    def teardown(self, recursive=False):
+        """ Close, or tear down, a device. """
+        log_method_call(self, self.name, status=self.status)
+        if not self.exists:
+            raise DeviceError("device has not been created")
+
+        if self.status:
+            self.teardownFormat()
+            self.slave.teardownFormat()
+
+        if recursive:
+            self.teardownParents(recursive=recursive)
+
+    @property
+    def slave(self):
+        """ This device's backing device. """
+        return self.parents[0]
+
+
+class VolumeGroupDevice(DMDevice):
+    """ An LVM Volume Group
+
+        XXX Maybe this should inherit from StorageDevice instead of
+            DMDevice since there's no actual device.
+    """
+    _type = "lvm vg"
+
+    def __init__(self, name, parents, size=None, free=None,
+                 peSize=None, peCount=None, peFree=None, pvCount=None,
+                 lvNames=[], uuid=None, exists=None, sysfsPath=''):
+        """ Create a VolumeGroupDevice instance.
+
+            Arguments:
+
+                name -- the device name (generally a device node's basename)
+                parents -- a list of physical volumes (StorageDevice)
+
+            Keyword Arguments:
+
+                peSize -- physical extent size (in MB)
+                exists -- indicates whether this is an existing device
+                sysfsPath -- sysfs device path
+
+                For existing VG's only:
+
+                    size -- the VG's size (in MB)
+                    free -- amount of free space in the VG
+                    peFree -- number of free extents
+                    peCount -- total number of extents
+                    pvCount -- number of PVs in this VG
+                    lvNames -- the names of this VG's LVs
+                    uuid -- the VG's UUID
+
+        """
+        self.pvClass = get_device_format_class("lvmpv")
+        if not self.pvClass:
+            raise DeviceError("cannot find 'lvmpv' class")
+
+        if isinstance(parents, list):
+            for dev in parents:
+                if not isinstance(dev.format, self.pvClass):
+                    raise ValueError("constructor requires a list of PVs")
+        elif not isinstance(parents.format, self.pvClass):
+            raise ValueError("constructor requires a list of PVs")
+
+        DMDevice.__init__(self, name, parents=parents,
+                          exists=exists, sysfsPath=sysfsPath)
+
+        self.uuid = uuid
+        self.free = numeric_type(free)
+        # XXX should pesize be in KB?
+        self.peSize = numeric_type(peSize)
+        self.peCount = numeric_type(peCount)
+        self.peFree = numeric_type(peFree)
+        self.pvCount = numeric_type(pvCount)
+        self.lvNames = lvNames
+
+        # circular references, here I come
+        self._lvs = []
+
+        # TODO: validate peSize if given
+        if not self.peSize:
+            self.peSize = 4.0   # MB
+
+        #self.probe()
+
+    def probe(self):
+        """ Probe for any information about this device. """
+        log_method_call(self, self.name, status=self.status)
+        if not self.exists:
+            raise DeviceError("device has not been created")
+
+    @property
+    def status(self):
+        """ The device's status (True means active). """
+        if not self.exists:
+            return False
+
+        # certainly if any of this VG's LVs are active then so are we
+        for lvName in self.lvNames:
+            lvPath = os.path.normpath("%s-%s" % (self.path, lvName))
+            if os.path.exists(lvPath):
+                return True
+
+        # if any of our PVs are not active then we cannot be
+        for pv in self.pvs:
+            if not pv.status:
+                return False
+
+        # if we are missing some of our PVs we cannot be active
+        if len(self.pvs) != self.pvCount:
+            return False
+
+        return True
+
+    def addDevice(self, device):
+        """ Add a new physical volume device to the volume group.
+
+            XXX This is for use by device probing routines and is not
+                intended for modification of the VG.
+        """
+        log_method_call(self,
+                        self.name,
+                        device=device.name,
+                        status=self.status)
+        if not self.exists:
+            raise DeviceError("device does not exist")
+
+        if not isinstance(device.format, self.pvClass):
+            raise ValueError("addDevice requires a PV arg")
+
+        if self.uuid and device.format.vgUuid != self.uuid:
+            raise ValueError("UUID mismatch")
+
+        if device in self.pvs:
+            raise ValueError("device is already a member of this VG")
+
+        self.pvs.append(device)
+        device.addChild()
+
+        # now see if the VG can be activated
+        if len(self.parents) == self.pvCount:
+            self.setup()
+
+    def removeDevice(self, device):
+        """ Remove a physical volume from the volume group.
+
+            This is for cases like clearing of preexisting partitions.
+        """
+        log_method_call(self,
+                        self.name,
+                        device=device.name,
+                        status=self.status)
+        try:
+            self.pvs.remove(device)
+        except ValueError, e:
+            raise ValueError("cannot remove non-member PV device from VG")
+
+        device.removeChild()
+
+    def setup(self):
+        """ Open, or set up, a device.
+
+            XXX we don't do anything like "vgchange -ay" because we don't
+                want all of the LVs activated, just the VG itself.
+        """
+        log_method_call(self, self.name, status=self.status)
+        if not self.exists:
+            raise DeviceError("device has not been created")
+
+        if self.status:
+            return
+
+        if len(self.parents) < self.pvCount:
+            raise DeviceError("cannot activate VG with missing PV(s)")
+
+        self.setupParents()
+
+    def teardown(self, recursive=None):
+        """ Close, or tear down, a device. """
+        log_method_call(self, self.name, status=self.status)
+        if not self.exists:
+            raise DeviceError("device has not been created")
+
+        if self.status:
+            lvm.vgdeactivate(self.name)
+
+        if recursive:
+            self.teardownParents(recursive=recursive)
+
+    def create(self):
+        """ Create the device. """
+        log_method_call(self, self.name, status=self.status)
+        if self.exists:
+            raise DeviceError("device already exists")
+
+        pv_list = []
+        #for pv in self.parents:
+            # This is a little bit different from other devices in that
+            # for VG we need the PVs to be formatted before we can create
+            # the VG.
+        #    pv.create()
+        #    pv.createFormat()
+        #    pv_list.append(pv.path)
+        pv_list = [pv.path for pv in self.parents]
+            
+        lvm.vgcreate(self.name, pv_list, self.peSize)
+        self.notifyKernel()
+        self.exists = True
+        self.setup()
+
+    def destroy(self):
+        """ Destroy the device. """
+        log_method_call(self, self.name, status=self.status)
+        if not self.exists:
+            raise DeviceError("device has not been created")
+
+        if self.status:
+            raise DeviceError("device is active")
+
+        lvm.vgremove(self.name)
+        self.notifyKernel()
+        self.exists = False
+        for parent in self.parents:
+            parent.removeChild()
+
+    def reduce(self, pv_list):
+        """ Remove the listed PVs from the VG. """
+        log_method_call(self, self.name, status=self.status)
+        if not self.exists:
+            raise DeviceError("device has not been created")
+
+        lvm.vgreduce(self.name, pv_list)
+        # XXX do we need to notify the kernel?
+
+    def addLogVol(self, lv):
+        """ Add an LV to this VG. """
+        # verify we have the space, then add it
+        if lv.size > self.freeSpace:
+            raise DeviceError("new lv is too large to fit in free space")
+
+        self._lvs.append(lv)
+
+    def removeLogVol(self, lv):
+        """ Remove an LV from this VG. """
+        if not lv in self.lvs:
+            raise ValueError("specified lv is not part of this vg")
+
+        self._lvs.remove(lv)
+
+    """ We can't rely on lvm to tell us about our size, free space, &c
+        since we could have modifications queued, unless the VG and all of
+        its PVs already exist.
+
+            -- liblvm may contain support for in-memory devices
+    """
+    @property
+    def isModified(self):
+        """ Return True if the VG has changes queued that LVM is unaware of. """
+        modified = True
+        if self.exists and not filter(lambda d: not d.exists, self.pvs):
+            modified = False
+
+        return modified
+
+    @property
+    def size(self):
+        """ The size of this VG """
+        # TODO: just ask lvm if isModified returns False
+
+        # sum up the sizes of the PVs and align to pesize
+        size = 0
+        for pv in self.pvs:
+            size += self.align(pv.size - pv.format.peStart)
+
+        return size
+
+    @property
+    def extents(self):
+        """ Number of extents in this VG """
+        # TODO: just ask lvm if isModified returns False
+
+        return self.size / self.peSize
+
+    @property
+    def freeSpace(self):
+        """ The amount of free space in this VG (in MB). """
+        # TODO: just ask lvm if isModified returns False
+
+        # total the sizes of any LVs
+        # FIXME: need to account for metadata
+        used = 0
+        for lv in self.lvs:
+            used += self.align(lv.size)
+
+        return self.size - used
+
+    @property
+    def freeExtents(self):
+        """ The number of free extents in this VG. """
+        # TODO: just ask lvm if isModified returns False
+
+        # FIXME: need to account for metadata
+        return self.freeSpace / self.peSize
+
+    def align(self, size):
+        """ Align a size to a multiple of physical extent size. """
+        size = numeric_type(size)
+
+        # we want Kbytes as a float for our math
+        size *= 1024.0
+        pesize = self.peSize * 1024.0
+        return long((math.floor(size / pesize) * pesize) / 1024)
+
+    @property
+    def pvs(self):
+        """ A list of this VG's PVs """
+        return self.parents[:]  # we don't want folks changing our list
+
+    @property
+    def lvs(self):
+        """ A list of this VG's LVs """
+        return self._lvs[:]     # we don't want folks changing our list
+
+
+class LogicalVolumeDevice(DMDevice):
+    """ An LVM Logical Volume """
+    _type = "lvm lv"
+
+    def __init__(self, name, vgdev, size=None, uuid=None,
+                 format=None, exists=None, sysfsPath='',
+                 grow=None, maxsize=None, percent=None):
+        """ Create a LogicalVolumeDevice instance.
+
+            Arguments:
+
+                name -- the device name (generally a device node's basename)
+                vgdev -- volume group (VolumeGroupDevice instance)
+
+            Keyword Arguments:
+
+                size -- the device's size (in MB)
+                uuid -- the device's UUID
+                sysfsPath -- sysfs device path
+                format -- a DeviceFormat instance
+                exists -- indicates whether this is an existing device
+
+                For new (non-existent) LVs only:
+
+                    grow -- whether to grow this LV
+                    maxsize -- maximum size for growable LV (in MB)
+                    percent -- percent of VG space to take
+
+        """
+        if isinstance(vgdev, list):
+            if len(vgdev) != 1:
+                raise ValueError("constructor requires a single VolumeGroupDevice instance")
+            elif not isinstance(vgdev[0], VolumeGroupDevice):
+                raise ValueError("constructor requires a VolumeGroupDevice instance")
+        elif not isinstance(vgdev, VolumeGroupDevice):
+            raise ValueError("constructor requires a VolumeGroupDevice instance")
+        DMDevice.__init__(self, name, size=size, format=format,
+                          sysfsPath=sysfsPath, parents=vgdev,
+                          exists=exists)
+
+        self.uuid = uuid
+
+        self.req_grow = None
+        self.req_max_size = 0
+        self.req_size = 0   
+        self.req_percent = 0
+
+        if not self.exists:
+            self.req_grow = grow
+            self.req_max_size = numeric_type(maxsize)
+            # XXX should we enforce that req_size be pe-aligned?
+            self.req_size = self._size
+            self.req_percent = numeric_type(percent)
+
+        # here we go with the circular references
+        self.vg.addLogVol(self)
+
+    def probe(self):
+        """ Probe for any missing information about this device.
+
+            size
+        """
+        raise NotImplementedError("probe method not defined for StorageDevice")
+
+    @property
+    def vg(self):
+        """ This Logical Volume's Volume Group. """
+        return self.parents[0]
+
+    @property
+    def name(self):
+        """ This device's name. """
+        return "%s-%s" % (self.vg.name, self._name)
+
+    @property
+    def lvname(self):
+        """ The LV's name (not including VG name). """
+        return self._name
+
+    def setup(self):
+        """ Open, or set up, a device. """
+        log_method_call(self, self.name, status=self.status)
+        if not self.exists:
+            raise DeviceError("device has not been created")
+
+        if self.status:
+            return
+
+        self.vg.setup()
+        lvm.lvactivate(self.vg.name, self._name)
+
+    def teardown(self, recursive=None):
+        """ Close, or tear down, a device. """
+        log_method_call(self, self.name, status=self.status)
+        if not self.exists:
+            raise DeviceError("device has not been created")
+
+        if self.status:
+            self.teardownFormat()
+            lvm.lvdeactivate(self.vg.name, self._name)
+
+        if recursive:
+            self.vg.teardown(recursive=recursive)
+
+    def create(self):
+        """ Create the device. """
+        log_method_call(self, self.name, status=self.status)
+        if self.exists:
+            raise DeviceError("device already exists")
+
+        self.createParents()
+        self.setupParents()
+
+        # should we use --zero for safety's sake?
+        lvm.lvcreate(self.vg.name, self._name, self.size)
+        self.exists = True
+        self.setup()
+
+    def destroy(self):
+        """ Destroy the device. """
+        log_method_call(self, self.name, status=self.status)
+        if not self.exists:
+            raise DeviceError("device has not been created")
+
+        if self.status:
+            raise DeviceError("device is active")
+
+        self.destroyFormat()
+        self.teardown()
+        lvm.lvremove(self.vg.name, self._name)
+        self.exists = False
+        for parent in self.parents:
+            parent.removeChild()
+
+    def resize(self):
+        # XXX should we set up anything before doing this?
+        # XXX resize format probably, right?
+        log_method_call(self, self.name, status=self.status)
+        if not self.exists:
+            raise DeviceError("device has not been created")
+
+        self.teardownFormat()
+        lvm.lvresize(self.vg.name, self._name, self.size)
+
+
+class MDRaidArrayDevice(StorageDevice):
+    """ An mdraid (Linux RAID) device.
+
+        Since this is derived from StorageDevice, not PartitionDevice, it
+        can be used to represent a partitionable device.
+    """
+    _type = "mdarray"
+
+    def __init__(self, name, level=None, minor=None, size=None,
+                 memberDevices=None, totalDevices=None, bitmap=False,
+                 uuid=None, format=None, exists=None,
+                 parents=[], sysfsPath=''):
+        """ Create a MDRaidArrayDevice instance.
+
+            Arguments:
+
+                name -- the device name (generally a device node's basename)
+
+            Keyword Arguments:
+
+                level -- the device's RAID level (a string, eg: '1' or 'raid1')
+                parents -- list of member devices (StorageDevice instances)
+                size -- the device's size (units/format TBD)
+                uuid -- the device's UUID
+                minor -- the device minor
+                bitmap -- whether to use a bitmap (boolean)
+                sysfsPath -- sysfs device path
+                format -- a DeviceFormat instance
+                exists -- indicates whether this is an existing device
+        """
+        StorageDevice.__init__(self, name, format=format, exists=exists,
+                               minor=minor, size=size,
+                               parents=parents, sysfsPath=sysfsPath)
+        self.level = level
+        self.uuid = uuid
+        self.memberDevices = numeric_type(memberDevices)
+        self.totalDevices = numeric_type(totalDevices)
+        self.sysfsPath = "/devices/virtual/block/%s" % name
+
+        """ FIXME: Bitmap is more complicated than this.
+
+            It can be internal or external. External requires a filename.
+        """
+        self.bitmap = bitmap
+
+        self.formatClass = get_device_format_class("mdmember")
+        if not self.formatClass:
+            raise DeviceError("cannot find class for 'mdmember'")
+
+        #self.probe()
+
+    @property
+    def mdadmConfEntry(self):
+        """ This array's mdadm.conf entry. """
+        if not self.level or self.memberDevices is None or not self.uuid:
+            raise DeviceError("array is not fully defined")
+
+        fmt = "ARRAY level=%s num-devices=%d UUID=%s"
+        return fmt % (self.level, self.memberDevices, self.uuid)
+
+    def getSpares(self):
+        spares = 0
+        if self.memberDevices is not None:
+            if self.totalDevices is not None:
+                spares = self.totalDevices - self.memberDevices
+            else:
+                spares = self.memberDevices
+                self.totalDevices = self.memberDevices
+        return spares
+
+    def setSpares(self, spares):
+        # FIXME: this is too simple to be right
+        if self.totalDevices > spares:
+            self.memberDevices = self.totalDevices - spares
+
+    spares = property(getSpares, setSpares)
+
+    def probe(self):
+        """ Probe for any missing information about this device.
+
+            I'd like to avoid paying any attention to "Preferred Minor"
+            as it seems problematic.
+        """
+        log_method_call(self, self.name, status=self.status)
+        if not self.exists:
+            raise DeviceError("device has not been created")
+
+        try:
+            self.devices[0].setup()
+        except Exception:
+            return
+
+        info = mdraid.mdexamine(self.devices[0].path)
+        if self.level is None:
+            self.level = info['level']
+        if self.memberDevices is None:
+            self.memberDevices = info['nrDisks']
+        if self.totalDevices is None:
+            self.totalDevices = info['totalDisks']
+        if self.uuid is None:
+            self.uuid = info['uuid']
+        if self.minor is None:
+            self.minor = info['mdMinor']
+
+    @property
+    def fstabSpec(self):
+        return self.path
+
+    def updateSysfsPath(self):
+        """ Update this device's sysfs path. """
+        log_method_call(self, self.name, status=self.status)
+        if not self.exists:
+            raise DeviceError("device has not been created")
+
+        if self.status:
+            self.sysfsPath = os.path.join("/sys",
+                                          self.sysfsBlockDir,
+                                          self.name)
+        else:
+            self.sysfsPath = None
+
+    def addDevice(self, device):
+        """ Add a new member device to the array.
+
+            XXX This is for use when probing devices, not for modification
+                of arrays.
+        """
+        log_method_call(self,
+                        self.name,
+                        device=device.name,
+                        status=self.status)
+        if not self.exists:
+            raise DeviceError("device has not been created")
+
+        if not isinstance(device.format, self.formatClass):
+            raise ValueError("invalid device format for mdraid member")
+
+        if self.uuid and device.format.mdUuid != self.uuid:
+            raise ValueError("cannot add member with non-matching UUID")
+
+        if device in self.devices:
+            raise ValueError("device is already a member of this array")
+
+        # we added it, so now set up the relations
+        self.devices.append(device)
+        device.addChild()
+
+    def removeDevice(self, device):
+        """ Remove a component device from the array.
+
+            XXX This is for use by clearpart, not for reconfiguration.
+        """
+        log_method_call(self,
+                        self.name,
+                        device=device.name,
+                        status=self.status)
+
+        if device not in self.devices:
+            raise ValueError("cannot remove non-member device from array")
+
+        self.devices.remove(device)
+        device.removeChild()
+
+    @property
+    def status(self):
+        """ This device's status.
+
+            For now, this should return a boolean:
+                True    the device is open and ready for use
+                False   the device is not open
+        """
+        # check the status in sysfs
+        status = False
+        if not self.exists:
+            return status
+
+        state_file = "/sys/%s/md/array_state" % self.sysfsPath
+        if os.access(state_file, os.R_OK):
+            state = open(state_file).read().strip()
+            log.debug("%s state is %s" % (self.name, state))
+            if state in ("clean", "active"):
+                status = True
+
+        return status
+
+    @property
+    def degraded(self):
+        """ Return True if the array is running in degraded mode. """
+        rc = False
+        degraded_file = "/sys/%s/md/degraded" % self.sysfsPath
+        if os.access(state_file, os.R_OK):
+            val = open(state_file).read().strip()
+            log.debug("%s degraded is %s" % (self.name, val))
+            if val == "1":
+                rc = True
+
+        return rc
+
+    @property
+    def devices(self):
+        """ Return a list of this array's member device instances. """
+        return self.parents
+
+    def setup(self):
+        """ Open, or set up, a device. """
+        log_method_call(self, self.name, status=self.status)
+        if not self.exists:
+            raise DeviceError("device has not been created")
+
+        if self.status:
+            return
+
+        disks = []
+        for member in self.devices:
+            member.setup()
+            disks.append(member.path)
+
+        mdraid.mdactivate(self.path,
+                          members=disks,
+                          super_minor=self.minor,
+                          uuid=self.uuid)
+
+        udev_settle()
+        # now we see what minor we got and update our name, &c accordingly
+
+    def teardown(self, recursive=None):
+        """ Close, or tear down, a device. """
+        log_method_call(self, self.name, status=self.status)
+        if not self.exists:
+            raise DeviceError("device has not been created")
+
+        if self.status:
+            self.teardownFormat()
+            mdraid.mddeactivate(self.path)
+            # The md layer doesn't do this, so we do it ourselves.
+            self.notifyKernel()
+
+        if recursive:
+            self.teardownParents(recursive=recursive)
+
+    def createFormat(self):
+        # create resident filesystem
+        log_method_call(self, self.name, status=self.status)
+        if not self.exists:
+            raise DeviceError("device has not been created")
+
+        if self.format:
+            formatArgs = []
+            if self.format.type == "ext2":
+                if self.level == 5:
+                    formatArgs = ['-R',
+                                  'stride=%d' % ((self.memberDevices - 1) * 16)]
+                elif self.level == 0:
+                    formatArgs = ['-R',
+                                  'stride=%d' % (self.memberDevices * 16)]
+
+            self.setup()
+            #self.format.create(options=formatArgs)
+            # FIXME: need to find a way to specify these options
+            self.format.create()
+
+    def create(self):
+        """ Create the device. """
+        log_method_call(self, self.name, status=self.status)
+        if self.exists:
+            raise DeviceError("device already exists")
+
+        disks = [disk.path for disk in self.devices]
+        self.createParents()
+        spares = len(self.devices) - self.memberDevices
+        mdraid.mdcreate(self.path,
+                        self.level,
+                        disks,
+                        spares)
+
+        self.notifyKernel()
+        self.exists = True
+        # the array is automatically activated upon creation, but...
+        self.setup()
+
+        # FIXME: we need to find our UUID now that the array exists
+
+    def destroy(self):
+        """ Destroy the device. """
+        log_method_call(self, self.name, status=self.status)
+        if not self.exists:
+            raise DeviceError("device has not been created")
+
+        if self.status:
+            raise DeviceError("device is active")
+
+        self.destroyFormat()
+        self.teardown()
+        # destruction of the formats on members will destroy the array
+        for disk in self.devices:
+            # should we do this ourselves?
+            disk.destroyFormat()
+
+        self.notifyKernel()
+        self.exists = False
+        for parent in self.parents:
+            parent.removeChild()
+
+
+class DMRaidArrayDevice(DMDevice):
+    """ A dmraid (device-mapper RAID) device """
+    _type = "dm-raid array"
+    _packages = ["dmraid"]
+
+    def __init__(self, name, format=None, size=None,
+                 exists=None, parents=[], sysfsPath=''):
+        """ Create a DMRaidArrayDevice instance.
+
+            Arguments:
+
+                name -- the device name (generally a device node's basename)
+
+            Keyword Arguments:
+
+                parents -- a list of the member devices
+                sysfsPath -- sysfs device path
+                size -- the device's size
+                format -- a DeviceFormat instance
+                exists -- indicates whether this is an existing device
+        """
+        if isinstance(parents, StorageDevice):
+            parents = [parents]
+
+        if isinstance(parents, list):
+            for parent in parents:
+                if not parent.format or parent.format.type != "dmraidmember":
+                    raise ValueError("parent devices must contain dmraidmember format")
+        DMDevice.__init__(self, name, format=format, size=size,
+                          parents=parents, sysfsPath=sysfsPath,
+                          exists=exists)
+
+    def probe(self):
+        """ Probe for any missing information about this device.
+
+            size
+        """
+        raise NotImplementedError("probe method not defined for DMRaidArrayDevice")
+
+
+class MultipathDevice(DMDevice):
+    """ A multipath device """
+    _type = "dm-multipath"
+    _packages = ["device-mapper-multipath"]
+
+    def __init__(self, name, format=None, size=None,
+                 exists=None, parents=[], sysfsPath=''):
+        """ Create a MultipathDevice instance.
+
+            Arguments:
+
+                name -- the device name (generally a device node's basename)
+
+            Keyword Arguments:
+
+                sysfsPath -- sysfs device path
+                size -- the device's size
+                format -- a DeviceFormat instance
+                parents -- a list of the backing devices (Device instances)
+                exists -- indicates whether this is an existing device
+        """
+        DMDevice.__init__(self, name, format=format, size=size,
+                          parents=parents, sysfsPath=sysfsPath,
+                          exists=exists)
+
+    def probe(self):
+        """ Probe for any missing information about this device.
+
+            size
+        """
+        raise NotImplementedError("probe method not defined for MultipathDevice")
+
+
+class NoDevice(StorageDevice):
+    """ A nodev device for nodev filesystems like tmpfs. """
+    _type = "nodev"
+
+    def __init__(self, format=None):
+        """ Create a NoDevice instance.
+
+            Arguments:
+
+            Keyword Arguments:
+
+                format -- a DeviceFormat instance
+        """
+        if format:
+            name = format.type
+        else:
+            name = "nodev"
+
+        StorageDevice.__init__(self, name, format=format)
+
+    @property
+    def path(self):
+        """ Device node representing this device. """
+        return self.name
+
+    def probe(self):
+        """ Probe for any missing information about this device. """
+        log_method_call(self, self.name, status=self.status)
+
+    def setup(self):
+        """ Open, or set up, a device. """
+        log_method_call(self, self.name, status=self.status)
+
+    def teardown(self, recursive=False):
+        """ Close, or tear down, a device. """
+        log_method_call(self, self.name, status=self.status)
+
+    def create(self):
+        """ Create the device. """
+        log_method_call(self, self.name, status=self.status)
+
+    def destroy(self):
+        """ Destroy the device. """
+        log_method_call(self, self.name, status=self.status)
+
+
+class FileDevice(StorageDevice):
+    """ A file on a filesystem.
+
+        This exists because of swap files.
+    """
+    _type = "file"
+    devDir = ""
+
+    def __init__(self, path, format=None, size=None,
+                 exists=None, parents=[]):
+        """ Create a FileDevice instance.
+
+            Arguments:
+
+                path -- full path to the file
+
+            Keyword Arguments:
+
+                format -- a DeviceFormat instance
+                size -- the file size (units TBD)
+                parents -- a list of required devices (Device instances)
+                exists -- indicates whether this is an existing device
+        """
+        StorageDevice.__init__(self, name, format=format, size=size,
+                               exists=exists, parents=parents)
+
+    def probe(self):
+        """ Probe for any missing information about this device. """
+        pass
+
+    @property
+    def fstabSpec(self):
+        return self.path
+
+    @property
+    def path(self):
+        path = self.name
+        root = ""
+        try:
+            status = self.parents[0].format.status
+        except AttributeError:
+            status = False
+
+        if status: 
+            # this is the actual active mountpoint
+            root = self.parents[0].format._mountpoint
+
+        return os.path.normpath("%s/%s" % (root, path))
+
+    def createFormat(self):
+        if self.format and not self.format.status:
+            self.format.device = self.path
+        StorageDevice.createFormat(self)
+
+    def setupFormat(self):
+        if self.format and self.format.exists and not self.format.status:
+            self.format.device = self.path
+        StorageDevice.setupFormat(self)
+
+    def teardown(self):
+        StorageDevice.teardown(self)
+        if self.format and self.format.exists and not self.format.status:
+            self.format.device = self.path
+
+    def create(self):
+        """ Create the device. """
+        log_method_call(self, self.name, status=self.status)
+        if self.exists:
+            raise DeviceError("device already exists")
+
+        # this only checks that parents exist
+        self.createParents()
+
+        try:
+            fd = os.open(self.path, os.O_RDWR)
+        except OSError as e:
+            raise DeviceError(e)
+
+        try:
+            buf = '\0' * 1024 * 1024 * self.size
+            os.write(fd, buf)
+        except Exception, e:
+            log.error("error zeroing out %s: %s" % (self.device, e))
+        finally:
+            os.close(fd)
+
+        self.exists = True
+
+    def destroy(self):
+        """ Destroy the device. """
+        log_method_call(self, self.name, status=self.status)
+        if not self.exists:
+            raise DeviceError("device has not been created")
+
+        self.destroyFormat()
+        os.unlink(self.path)
+        self.exists = False
+        for parent in self.parents:
+            parent.removeChild()
+
+
+class DirectoryDevice(FileDevice):
+    """ A directory on a filesystem.
+
+        This exists because of bind mounts.
+    """
+    _type = "directory"
+
+    def create(self):
+        """ Create the device. """
+        log_method_call(self, self.name, status=self.status)
+        if self.exists:
+            raise DeviceError("device already exists")
+
+        self.createParents()
+        self.setupParents()
+        try:
+            iutil.mkdirChain(self.path)
+        except Exception, e:
+            raise DeviceError, e
+
+        self.exists = True
+
+    def destroy(self):
+        """ Destroy the device. """
+        log_method_call(self, self.name, status=self.status)
+        if not self.exists:
+            raise DeviceError("device has not been created")
+
+        os.unlink(self.path)
+        self.exists = False
+        for parent in self.parents:
+            parent.removeChild()
+
+
+class iScsiDiskDevice(DiskDevice):
+    """ An iSCSI volume/device/target/???.
+
+        TODO: learn what this is and how we need to use it.
+    """
+    _type = "iscsi"
+
+    def __init__(self, ipaddr, port,
+                 user=None, passwd=None,
+                 user_in=None, passwd_in=None,
+                 major=None, minor=None, size=None,
+                 exists=None, parents=[], sysfsPath=''):
+        name = "iscsi://%s:%s" % (ipaddr, port)
+        DiskDevice.__init__(self, name, size=size,
+                            major=major, minor=minor, exists=exists,
+                            parents=parents, sysfsPath=sysfsPath)
+
+    def probe(self):
+        """ Probe for any missing information about this device. """
+        raise NotImplementedError("probe method not defined for StorageDevice")
+
+
+class OpticalDevice(StorageDevice):
+    """ An optical drive, eg: cdrom, dvd+r, &c.
+
+        XXX Is this useful?
+    """
+    _type = "cdrom"
+
+    def __init__(self, name, major=None, minor=None, exists=None,
+                 format=None, parents=[], sysfsPath=''):
+        StorageDevice.__init__(self, name, format=format,
+                               major=major, minor=minor, exists=True,
+                               parents=parents, sysfsPath=sysfsPath)
+
+    def teardown(self, recursive=None):
+        """ Close, or tear down, a device. """
+        log_method_call(self, self.name, status=self.status)
+        if not self.exists:
+            raise DeviceError("device has not been created")
+
+        self.teardownFormat()
+        if recursive:
+            self.teardownParents(recursive=recursive)
+
+    def mediaPresent(self):
+        """ Return a boolean indicating whether or not the device contains
+            media.
+        """
+        log_method_call(self, self.name, status=self.status)
+        if not self.exists:
+            raise DeviceError("device has not been created")
+
+    def eject(self):
+        """ Eject the drawer. """
+        log_method_call(self, self.name, status=self.status)
+        if not self.exists:
+            raise DeviceError("device has not been created")
+
+
+class ZFCPDiskDevice(DiskDevice):
+    """ A mainframe ZFCP disk. """
+    _type = "zfcp"
+
+    def __init__(self, name, size=None, major=None, minor=None,
+                 devnum=None, wwpn=None, fcplun=None,
+                 parents=[], sysfsPath=''):
+        self.devnum = devnum
+        self.wwpn = wwpn
+        self.fcplun = fcplun
+        name = "zfcp://%s/%s/%s" % (self.devnum, self.wwpn, self.fcplun)
+        DiskDevice.__init__(self, name, size=size,
+                            major=major, minor=minor,
+                            parents=parents, sysfsPath=sysfsPath)
+
+    def probe(self):
+        """ Probe for any missing information about this device.
+
+            devnum, wwpn, fcplun
+        """
+        raise NotImplementedError("probe method not defined for StorageDevice")
+
+
+class DASDDevice(DiskDevice):
+    """ A mainframe DASD. """
+    _type = "dasd"
+
+    def __init__(self, device, size=None, major=None, minor=None,
+                 parents=[], sysfsPath=''):
+        DiskDevice.__init__(self, device, size=size,
+                            major=major, minor=minor,
+                            parents=parents, sysfsPath=sysfsPath)
+
+    def probe(self):
+        """ Probe for any missing information about this device. """
+        raise NotImplementedError("probe method not defined for StorageDevice")
+
+
+class PRePBootDevice(PartitionDevice):
+    """ A PPC PReP boot partition.
+
+        XXX Would this be better represented by a DeviceFormat class?
+    """
+    _type = "PReP"
+    #_partedFlags = parted.PARTITION_PREP
+
+    def __init__(self, device,
+                 size=None, grow=False, maxsize=None,
+                 major=None, minor=None,
+                 sysfsPath='', parents=[],
+                 exists=None, primary=False):
+        """ Create a PRePBootDevice instance.
+
+            Arguments:
+
+                device -- the device name (generally a device node's basename)
+
+            Keyword Arguments:
+
+                grow -- whether or not to grow the partition (boolean )
+                maxsize -- max size for growable partitions (units TBD)
+                size -- the device's size (units/format TBD)
+                major -- the device major
+                minor -- the device minor
+                sysfsPath -- sysfs device path
+                parents -- a list of required Device instances
+                exists -- indicates whether this is an existing device
+        """
+        PartitionDevice.__init__(self, device, partType=self._partType,
+                                 size=size, grow=grow, maxsize=maxsize,
+                                 major=major, minor=minor,
+                                 sysfsPath=sysfsPath, exists=exists,
+                                 parents=parents, primary=primary)
+
+
+class NFSDevice(StorageDevice):
+    """ An NFS device """
+    _type = "nfs"
+
+    def __init__(self, device, format=None, parents=[]):
+        # we could make host/ip, path, &c but will anything use it?
+        StorageDevice.__init__(device, format=format, parents=parents)
+
+    @property
+    def path(self):
+        """ Device node representing this device. """
+        return self.name
+
+    def setup(self):
+        """ Open, or set up, a device. """
+        log_method_call(self, self.name, status=self.status)
+
+    def teardown(self, recursive=None):
+        """ Close, or tear down, a device. """
+        log_method_call(self, self.name, status=self.status)
+
+    def create(self):
+        """ Create the device. """
+        log_method_call(self, self.name, status=self.status)
+
+    def destroy(self):
+        """ Destroy the device. """
+        log_method_call(self, self.name, status=self.status)
+
+
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/storage/devicetree.py anaconda-storage/storage/devicetree.py
--- anaconda/storage/devicetree.py	1969-12-31 18:00:00.000000000 -0600
+++ anaconda-storage/storage/devicetree.py	2009-02-17 13:10:19.000000000 -0600
@@ -0,0 +1,874 @@
+# devicetree.py
+# Device management for anaconda's storage configuration module.
+#
+# Copyright (C) 2009  Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
+# Public License for more details.  You should have received a copy of the
+# GNU General Public License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.  Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): Dave Lehman <dlehman@redhat.com>
+#
+
+import os
+
+import sys
+sys.path.append("formats")
+
+if __name__ == "__main__":
+    import storage_log
+
+from errors import *
+from devices import *
+from deviceaction import *
+from deviceformat import getFormat
+from udev import *
+
+import gettext
+_ = lambda x: gettext.ldgettext("anaconda", x)
+
+import logging
+log = logging.getLogger("storage")
+
+
+class DeviceTree(object):
+    """ A quasi-tree that represents the devices in the system.
+
+        The tree contains a list of device instances, which does not
+        necessarily reflect the actual state of the system's devices.
+        DeviceActions are used to perform modifications to the tree,
+        except when initially populating the tree.
+
+        DeviceAction instances are registered, possibly causing the
+        addition or removal of Device instances to/from the tree. The
+        DeviceActions are all reversible up to the time their execute
+        method has been called.
+
+        Only one action of any given type/object pair should exist for
+        any given device at any given time.
+
+        DeviceAction instances can only be registered for leaf devices,
+        except for resize actions.
+    """
+
+    def __init__(self, anaconda):
+        # internal data members
+        self._devices = []
+        self._actions = []
+
+        # FIXME: ignored disks
+
+    def processActions(self, dryRun=None):
+        """ Execute all registered actions. """
+        def cmpActions(x, y):
+            """
+                < 1 => x < y
+                  0 => x == y
+                > 1 => x > y
+            """
+	    #log.debug("%s | %s" % (x, y))
+
+            # destroy actions come first
+            if x.isDestroy() and not y.isDestroy():
+		#log.debug("x is destroy -- x first")
+                return -1
+            elif y.isDestroy() and not x.isDestroy():
+		#log.debug("y is destroy -- y first")
+                return 1
+            elif x.isDestroy() and y.isDestroy():
+                # outermost/youngest first
+                if x.device.dependsOn(y.device):
+                    #log.debug("x depends on y -- x first")
+                    return -1
+                elif y.device.dependsOn(x.device):
+                    #log.debug("y depends on x -- y first")
+                    return 1
+                # filesystem destruction must precede device destruction
+                elif x.isFormat() and not y.isFormat():
+		    #log.debug("x is format -- x first")
+                    return -1
+                elif y.isFormat() and not x.isFormat():
+                    #log.debug("y is format -- y first")
+                    return 1
+
+            # resize actions come next
+            if x.isResize() and not y.isResize():
+		#log.debug("x is resize -- x first")
+                return -1
+            elif y.isResize() and not x.isResize():
+	        #log.debug("y is resize -- y first")
+                return 1
+            elif x.isResize() and y.isResize():
+                if x.isGrow() and y.isGrow():
+                    # for grow, devices come first, root down
+                    if x.isDevice() and not y.isDevice():
+                        #log.debug("x is device -- x first")
+                        return -1
+                    elif y.isDevice() and not x.isDevice():
+			#log.debug("y is device -- y first")
+                        return 1
+                    else:
+                        # innermost/oldest first
+                        if x.device.dependsOn(y.device):
+			    #log.debug("x depends on y -- y first")
+                            return 1
+                        elif y.device.dependsOn(x.device):
+		            #log.debug("y depends on x -- x first")
+                            return -1
+                elif x.isShrink() and y.isShrink():
+                    # for shrink, filesystems come first, leaves up
+                    if x.isFormat() and not y.isFormat():
+		        #log.debug("x is format -- x first")
+                        return -1
+                    elif y.isFormat() and not x.isFormat():
+                        #log.debug("y is format -- y first")
+                        return 1
+                    else:
+                        # outermost/youngest first
+                        if x.device.dependsOn(y.device):
+			    #log.debug("x depends on y -- x first")
+                            return -1
+                        elif y.device.dependsOn(x.device):
+                            #log.debug("y depends on x -- y first")
+                            return 1
+                else:
+                    # we don't care about grow action -v- shrink action
+                    # since they should be unrelated
+		    #log.debug("we don't care")
+                    return 0
+
+            # create actions come last
+            if x.isCreate() and y.isCreate():
+                # innermost/oldest first
+                if x.device.dependsOn(y.device):
+		    #log.debug("x depends on y")
+                    return 1
+                elif y.device.dependsOn(x.device):
+		    #log.debug("y depends on x")
+                    return -1
+                # devices first, root down
+                elif x.isDevice() and not y.isDevice():
+		    #log.debug("x is a device")
+                    return -1
+                elif y.isDevice() and not x.isDevice():
+		    #log.debug("y is a device")
+                    return 1
+		elif x.device.isleaf and not y.device.isleaf:
+		    #log.debug("x is a leaf -- y first")
+                    return 1
+                elif y.device.isleaf and not x.device.isleaf:
+		    #log.debug("y is a leaf -- x first")
+                    return -1
+
+	    #log.debug("no decision")
+            return 0
+
+        # in most cases the actions will already be sorted because of the
+        # rules for registration, but let's not rely on that
+        self._actions.sort(cmpActions)
+        for action in self._actions:
+            print "executing action: %s" % action
+            if not dryRun:
+                action.execute()
+
+    def _addDevice(self, newdev):
+        """ Add a device to the tree.
+
+            Raise ValueError if the device's identifier is already
+            in the list.
+        """
+        if newdev.path in [d.path for d in self._devices]:
+            raise ValueError("device is already in tree")
+
+        # make sure this device's parent devices are in the tree already
+        for parent in newdev.parents:
+            if parent not in self._devices:
+                raise DeviceTreeError("parent device not in tree")
+
+        self._devices.append(newdev)
+        log.debug("added %s (%s) to device tree" % (newdev.name,
+                                                    newdev.type))
+
+    def _removeDevice(self, dev, force=None):
+        """ Remove a device from the tree.
+
+            XXX only leaves may be removed.
+        """
+        if dev not in self._devices:
+            raise ValueError("Device '%s' not in tree" % dev.name)
+
+        if not dev.isleaf and not force:
+            log.debug("%s has %d kids" % (dev.name, dev.kids))
+            raise ValueError("Cannot remove non-leaf device '%s'" % dev.name)
+
+        # if this is a partition we need to remove it from the parted.Disk
+        if dev.type == "partition":
+            dev.disk.removePartition(dev)
+
+        self._devices.remove(dev)
+        log.debug("removed %s (%s) from device tree" % (dev.name,
+                                                        dev.type))
+
+        for parent in dev.parents:
+            # Will this cause issues with garbage collection?
+            #   Do we care about garbage collection? At all?
+            parent.removeChild()
+
+    def registerAction(self, action):
+        """ Register an action to be performed at a later time.
+
+            Modifications to the Device instance are handled before we
+            get here.
+        """
+        for _action in self._actions:
+            if _action.device == action.device and \
+               _action.type == action.type and \
+               _action.obj == action.obj:
+                raise DeviceTreeError("duplicate action for this device")
+
+        if (action.isDestroy() or action.isResize() or \
+            (action.isCreate() and action.isFormat())) and \
+           action.device not in self._devices:
+            raise DeviceTreeError("device is not registered")
+        elif (action.isCreate() and action.isDevice()) and \
+             action.device in self._devices:
+            raise DeviceTreeError("device is already registered")
+
+        if action.isCreate() and action.isDevice():
+            self._addDevice(action.device)
+        elif action.isDestroy() and action.isDevice():
+            self._removeDevice(action.device)
+        elif action.isCreate() and action.isFormat():
+            # FIXME: Make a better way to tell filesystems from other formats
+            if action.device.format and action.device.format.mountable and \
+               action.device.format.mountpoint in self.filesystems:
+                raise DeviceTreeError("mountpoint already in use")
+
+        print "registered action: %s" % action
+        self._actions.append(action)
+
+    def cancelAction(self, action):
+        """ Cancel a registered action.
+
+            This will unregister the action and do any required
+            modifications to the device list.
+
+            Actions all operate on a Device, so we can use the devices
+            to determine dependencies.
+        """
+        pass
+
+    def getDependentDevices(self, dep):
+        """ Return a list of devices that depend on dep.
+
+            The list includes both direct and indirect dependents.
+        """
+        dependents = []
+        for device in self.devices.values():
+            if device.dependsOn(dep):
+                dependents.append(device)
+
+        return dependents
+
+    def isIgnored(self, info):
+        """ Return True if info is a device we should ignore.
+
+            Arguments:
+
+                info -- a dict representing a udev db entry
+
+            TODO:
+
+                - filtering of SAN/FC devices
+                - filtering by driver?
+
+        """
+        sysfs_path = udev_device_get_sysfs_path(info)
+        name = udev_device_get_name(info)
+        if not sysfs_path:
+            return None
+
+        if name in self.ignoredDisks:
+            return True
+
+        for ignored in self.ignoredDisks:
+            if ignored == os.path.basename(os.path.dirname(sysfs_path)):
+                # this is a partition on a disk in the ignore list
+                return True
+
+        # FIXME: check for virtual devices whose slaves are on the ignore list
+
+    def addUdevDevice(self, info):
+        name = udev_device_get_name(info)
+        uuid = udev_device_get_uuid(info)
+        sysfs_path = udev_device_get_sysfs_path(info)
+        device = None
+
+        if self.isIgnored(sysfs_path):
+            log.debug("ignoring %s (%s)" % (name, sysfs_path))
+            return
+
+        log.debug("scanning %s (%s)..." % (name, sysfs_path))
+
+        #
+        # The first step is to either look up or create the device
+        #
+        if udev_device_is_dm(info):
+            log.debug("%s is a device-mapper device" % name)
+            # try to look up the device
+            device = self.getDeviceByName(name)
+            if device is None and uuid:
+                # try to find the device by uuid
+                device = self.getDeviceByUuid(uuid)
+
+            if device is None:
+                for dmdev in self.devices:
+                    if not isinstance(dmdev, DMDevice):
+                        continue
+
+                    # there is a device in the tree already with the same
+                    # major/minor as this one but with a different name
+                    # XXX this is kind of racy
+                    if dmdev.getDMNode() == os.path.basename(sysfs_path):
+                        # XXX should we take the name already in use?
+                        device = dmdev
+                        break
+
+            if device is None:
+                # we couldn't find it, so create it
+                # first, get a list of the slave devs and look them up
+                slaves = []
+                dir = os.path.normpath("/sys/%s/slaves" % sysfs_path)
+                slave_names = os.listdir(dir)
+                for slave_name in slave_names:
+                    # if it's a dm-X name, resolve it to a map name first
+                    if slave_name.startswith("dm-"):
+                        #slave_name = block.getNameFromDmNode(slave_name)
+                        slave_name = dm.name_from_dm_node(slave_name)
+                    slave_dev = self.getDeviceByName(slave_name)
+                    if slave_dev:
+                        slaves.append(slave_dev)
+                    else:
+                        # we haven't scanned the slave yet, so do it now
+                        path = os.path.normpath("%s/%s" % (dir, slave_name))
+                        new_info = udev_get_block_device(os.path.realpath(path))
+                        if new_info:
+                            self.addUdevDevice(new_info)
+                            device = self.getDeviceByName(name)
+                            if device is None:
+                                # if the current device is still not in
+                                # the tree, something has gone wrong
+                                log.error("failure scanning device %s" % name)
+                                return
+
+                # if we get here, we found all of the slave devices and
+                # something must be wrong -- if all of the slaves are in
+                # the tree, this device should be as well
+                if device is None:
+                    log.warning("using generic DM device for %s" % name)
+                    device = DMDevice(name, exists=True, parents=slaves)
+                    self._addDevice(device)
+        elif udev_device_is_md(info):
+            log.debug("%s is an md device" % name)
+            # try to look up the device
+            device = self.getDeviceByName(name)
+            if device is None and uuid:
+                # try to find the device by uuid
+                device = self.getDeviceByUuid(uuid)
+
+            if device is None:
+                # we didn't find a device instance, so we will create one
+                slaves = []
+                dir = os.path.normpath("/sys/%s/slaves" % sysfs_path)
+                slave_names = os.listdir(dir)
+                for slave_name in slave_names:
+                    # if it's a dm-X name, resolve it to a map name
+                    if slave_name.startswith("dm-"):
+                        #slave_name = block.getNameFromDmNode(slave_name)
+                        slave_name = dm.name_from_dm_node(slave_name)
+                    slave_dev = self.getDeviceByName(slave_name)
+                    if slave_dev:
+                        slaves.append(slave_dev)
+                    else:
+                        # we haven't scanned the slave yet, so do it now
+                        path = os.path.normpath("%s/%s" % (dir, slave_name))
+                        new_info = udev_get_block_device(os.path.realpath(path))
+                        if new_info:
+                            self.addUdevDevice(new_info)
+                            device = self.getDeviceByName(name)
+                            if device is None:
+                                # if the current device is still not in
+                                # the tree, something has gone wrong
+                                log.error("failure scanning device %s" % name)
+                                return
+
+                # if we get here, we found all of the slave devices and
+                # something must be wrong -- if all of the slaves we in
+                # the tree, this device should be as well
+                if device is None:
+                    log.warning("using MD RAID device for %s" % name)
+                    try:
+                        # level is reported as, eg: "raid1"
+                        md_level = udev_device_get_md_level(info)
+                        md_devices = int(udev_device_get_md_devices(info))
+                        md_uuid = udev_device_get_md_uuid(info)
+                    except (KeyError, IndexError, ValueError) as e:
+                        log.warning("invalid data for %s: %s" % (name, e))
+                        return
+
+                    device = MDRaidArrayDevice(name,
+                                               level=md_level,
+                                               memberDevices=md_devices,
+                                               uuid=md_uuid,
+                                               exists=True,
+                                               parents=slaves)
+                    self._addDevice(device)
+        elif udev_device_is_cdrom(info):
+            log.debug("%s is a cdrom" % name)
+            device = self.getDeviceByName(name)
+            if device is None:
+                # XXX should this be RemovableDevice instead?
+                #
+                # Looks like if it has ID_INSTANCE=0:1 we can ignore it.
+                device = OpticalDevice(name,
+                                       major=udev_device_get_major(info),
+                                       minor=udev_device_get_minor(info),
+                                       sysfsPath=sysfs_path)
+                self._addDevice(device)
+        elif udev_device_is_disk(info):
+            log.debug("%s is a disk" % name)
+            device = self.getDeviceByName(name)
+            if device is None:
+                device = DiskDevice(name,
+                                    major=udev_device_get_major(info),
+                                    minor=udev_device_get_minor(info),
+                                    sysfsPath=sysfs_path)
+                self._addDevice(device)
+        elif udev_device_is_partition(info):
+            log.debug("%s is a partition" % name)
+            device = self.getDeviceByName(name)
+            if device is None:
+                disk_name = os.path.basename(os.path.dirname(sysfs_path))
+                disk = self.getDeviceByName(disk_name)
+
+                if disk is None:
+                    # create a device instance for the disk
+                    path = os.path.dirname(os.path.realpath(sysfs_path))
+                    new_info = udev_get_block_device(path)
+                    if new_info:
+                        self.addUdevDevice(new_info)
+                        disk = self.getDeviceByName(disk_name)
+
+                    if disk is None:
+                        # if the current device is still not in
+                        # the tree, something has gone wrong
+                        log.error("failure scanning device %s" % disk_name)
+                        return
+
+                device = PartitionDevice(name,
+                                         sysfsPath=sysfs_path,
+                                         major=udev_device_get_major(info),
+                                         minor=udev_device_get_minor(info),
+                                         exists=True,
+                                         parents=[disk])
+                self._addDevice(device)
+
+        #
+        # now set the format
+        #
+        format = None
+        format_type = udev_device_get_format(info)
+        label = udev_device_get_label(info)
+        if device and format_type and not device.format:
+            args = [format_type]
+            kwargs = {"uuid": uuid,
+                      "label": label,
+                      "device": device.path,
+                      "exists": True}
+
+            if format_type == "swap":
+                # swap
+                pass
+            elif format_type == "crypto_LUKS":
+                # luks/dmcrypt
+                pass
+            elif format_type == "linux_raid_member":
+                # mdraid
+                kwargs["mdUuid"] = udev_device_get_md_uuid(info)
+            elif format_type == "isw_raid_member":
+                # dmraid
+                # TODO: collect name of containing raidset
+                # TODO: implement dmraid member format class
+                pass
+            elif format_type == "LVM2_member":
+                # lvm
+                try:
+                    kwargs["vgName"] = udev_device_get_vg_name(info)
+                except KeyError as e:
+                    log.debug("PV %s has no vg_name" % name)
+                kwargs["vgUuid"] = udev_device_get_vg_uuid(info)
+                kwargs["peStart"] = udev_device_get_pv_pe_start(info)
+
+            format = getFormat(*args, **kwargs)
+            device.setFormat(format)
+
+        #
+        # now lookup or create any compound devices we have discovered
+        #        
+        if format:
+            if format.type == "luks":
+                # look up or create the mapped device
+                map_name = "luks-%s" % format.uuid
+                if not self.getDeviceByName(map_name):
+                    luks_device = LUKSDevice(map_name,
+                                             parents=[device],
+                                             exists=True)
+                    self._addDevice(luks_device)
+                    try:
+                        luks_device.setup()
+                    except DeviceError as e:
+                        log.info("setup of %s failed: %s" % (map_name, e))
+                else:
+                    log.warning("luks device %s already in the tree" % map_name)
+            elif format.type == "mdmember":
+                # either look up or create the array device
+                md_array = self.getDeviceByUuid(format.mdUuid)
+                if format.mdUuid and md_array:
+                    md_array._addDevice(device)
+                else:
+                    # create the array with just this one member
+                    # FIXME: why does this exact block appear twice?
+                    try:
+                        # level is reported as, eg: "raid1"
+                        md_level = udev_device_get_md_level(info)
+                        md_devices = int(udev_device_get_md_devices(info))
+                        md_uuid = udev_device_get_md_uuid(info)
+                    except (KeyError, ValueError) as e:
+                        log.warning("invalid data for %s: %s" % (name, e))
+                        return
+
+                    # find the first unused minor
+                    minor = 0
+                    while True:
+                        if self.getDeviceByName("md%d" % minor):
+                            minor += 1
+                        else:
+                            break
+
+                    md_name = "md%d" % minor
+                    md_array = MDRaidArrayDevice(md_name,
+                                                 level=md_level,
+                                                 minor=minor,
+                                                 memberDevices=md_devices,
+                                                 uuid=md_uuid,
+                                                 exists=True,
+                                                 parents=[device])
+                    self._addDevice(md_array)
+                    try:
+                        md_array.setup()
+                    except DeviceError as e:
+                        log.info("setup of %s failed: %s" % (md_array.name, e))
+            elif format.type == "dmraidmember":
+                # look up or create the dmraid array
+                pass
+            elif format.type == "lvmpv":
+                # lookup/create the VG and LVsA
+                try:
+                    vg_name = udev_device_get_vg_name(info)
+                except KeyError:
+                    # no vg name means no vg -- we're done with this pv
+                    return
+
+                vg_device = self.getDeviceByName(vg_name)
+                if vg_device:
+                    vg_device._addDevice(device)
+                else:
+                    try:
+                        vg_uuid = udev_device_get_vg_uuid(info)
+                        vg_size = udev_device_get_vg_size(info)
+                        vg_free = udev_device_get_vg_free(info)
+                        pe_size = udev_device_get_vg_extent_size(info)
+                        pe_count = udev_device_get_vg_extent_count(info)
+                        pe_free = udev_device_get_vg_free_extents(info)
+                        pv_count = udev_device_get_vg_pv_count(info)
+                    except (KeyError, ValueError) as e:
+                        log.warning("invalid data for %s: %s" % (name, e))
+                        return
+
+                    vg_device = VolumeGroupDevice(vg_name,
+                                                  device,
+                                                  uuid=vg_uuid,
+                                                  size=vg_size,
+                                                  free=vg_free,
+                                                  peSize=pe_size,
+                                                  peCount=pe_count,
+                                                  peFree=pe_free,
+                                                  pvCount=pv_count,
+                                                  exists=True)
+                    self._addDevice(vg_device)
+
+                    # this should only get run once, right after instantiating
+                    # the VG
+                    try:
+                        lv_names = udev_device_get_lv_names(info)
+                        lv_uuids = udev_device_get_lv_uuids(info)
+                        lv_sizes = udev_device_get_lv_sizes(info)
+                    except KeyError as e:
+                        log.warning("invalid data for %s: %s" % (name, e))
+                        return
+
+                    if not lv_names:
+                        log.debug("no LVs listed for VG %s" % name)
+                        return
+
+                    lvs = []
+                    for (index, lv_name) in enumerate(lv_names):
+                        lv_dev = self.getDeviceByName("%s-%s" % (vg_name, lv_name))
+                        if lv_dev is None:
+                            lv_uuid = lv_uuids[index]
+                            lv_size = lv_sizes[index]
+                            lv_device = LogicalVolumeDevice(lv_name,
+                                                            vg_device,
+                                                            uuid=lv_uuid,
+                                                            size=lv_size,
+                                                            exists=True)
+                            self._addDevice(lv_device)
+                            try:
+                                lv_device.setup()
+                            except DeviceError as e:
+                                log.info("setup of %s failed: %s" % (lv_device.name,
+                                                                     e))
+
+    def _populate(self):
+        """ Locate all storage devices. """
+        # each iteration scans any devices that have appeared since the
+        # previous iteration
+        old_devices = []
+        ignored_devices = []
+        while True:
+            devices = []
+            new_devices = udev_get_block_devices()
+
+            for new_device in new_devices:
+                found = False
+                for old_device in old_devices:
+                    if old_device['name'] == new_device['name']:
+                        found = True
+                        break
+
+                if not found:
+                    devices.append(new_device)
+
+            if len(devices) == 0:
+                # nothing is changing -- we are finished building devices
+                break
+
+            old_devices = new_devices
+            log.debug("devices to scan: %s" % [d['name'] for d in devices])
+            for dev in devices:
+                self.addUdevDevice(dev)
+
+    def teardownAll(self):
+        """ Run teardown methods on all devices. """
+        for device in self.leaves:
+            try:
+                device.teardown(recursive=True)
+            except DeviceError as e:
+                log.info("teardown of %s failed: %s" % (device.name, e))
+
+    def setupAll(self):
+        """ Run setup methods on all devices. """
+        for device in self.leaves:
+            try:
+                device.setup()
+            except DeviceError as e:
+                log.debug("setup of %s failed: %s" % (device.name, e))
+
+    def getDeviceBySysfsPath(self, path):
+        found = None
+        for device in self._devices:
+            if device.sysfsPath == path:
+                found = device
+                break
+
+        return found
+
+    def getDeviceByUuid(self, uuid):
+        found = None
+        for device in self._devices:
+            if device.uuid == uuid:
+                found = device
+                break
+            elif device.format and device.format.uuid == uuid:
+                found = device
+                break
+
+        return found
+
+    def getDeviceByName(self, name):
+        log.debug("looking for device '%s'..." % name)
+        found = None
+        for device in self._devices:
+            if device.name == name:
+                found = device
+                break
+
+        log.debug("found %s" % found)
+        return found
+
+    def getDevicesByType(self, device_type):
+        # TODO: expand this to catch device format types
+        return [d for d in self._devices if d.type == device_type]
+
+    @property
+    def devices(self):
+        """ Dict with device path keys and Device values. """
+        devices = {}
+
+        for device in self._devices:
+            if device.path in devices:
+                raise DeviceTreeError("duplicate paths in device tree")
+
+            devices[device.path] = device
+
+        return devices
+
+    @property
+    def filesystems(self):
+        """ List of filesystems. """
+        #""" Dict with mountpoint keys and filesystem values. """
+        filesystems = []
+        for dev in self.leaves:
+            if dev.format and getattr(dev.format, 'mountpoint', None):
+                filesystems.append(dev.format)
+
+        return filesystems
+
+    @property
+    def uuids(self):
+        """ Dict with uuid keys and Device values. """
+        uuids = {}
+        for dev in self._devices:
+            try:
+                uuid = dev.uuid
+            except AttributeError:
+                uuid = None
+            elif uuid:
+                uuids[uuid] = dev
+
+            try:
+                uuid = dev.format.uuid
+            except AttributeError:
+                uuid = None
+            elif uuid:
+                uuids[uuid] = dev
+
+        return uuids
+
+    @property
+    def labels(self):
+        """ Dict with label keys and Device values.
+
+            FIXME: duplicate labels are a possibility
+        """
+        labels = {}
+        for dev in self._devices:
+            if dev.format and getattr(dev.format, "label", None):
+                labels[dev.format.label] = dev
+
+        return labels
+
+    @property
+    def leaves(self):
+        """ List of all devices upon which no other devices exist. """
+        leaves = [d for d in self._devices if d.isleaf]
+        return leaves
+
+    def getChildren(self, device):
+        """ Return a list of a device's children. """
+        return [c for c in self._devices if device in c.parents]
+
+
+def test_tree():
+    try:
+        tree = DeviceTree(ignored_disks=[])
+    except Exception as e:
+        log.error("tree creation failed: %s" % e)
+        raise
+    return tree
+
+def test_fstab(tree):
+    roots = findExistingRoots(tree, keepall=True)
+    print ["%s: %s" % (d.path, d.format.type) for d in roots]
+    rootDev = roots[0]
+    if not rootDev:
+        return
+
+    log.debug("setting up root device %s" % rootDev.path)
+    #rootDev.setup()
+    #rootDev.format.mount(chroot="/mnt/sysimage", mountpoint="/")
+    #fstab = FSTab(tree, chroot="/mnt/sysimage")
+    fsset = FSSet(tree)
+    m
+    #return fstab
+
+if __name__ == "__main__":
+    mode = "tree"
+    if len(sys.argv) == 2:
+        mode = sys.argv[1]
+
+    if mode == "tree":
+        tree = test_tree()
+        if tree is None:
+            sys.exit(1)
+        devices = tree.devices.values()
+        devices.sort(key=lambda d: d.path)
+        for device in devices:
+            fs_string = ""
+            if device.format:
+                fs_string = "%s on " % device.format.type
+            print "%s: %s%s" % (device.path, fs_string, device.typeDescription) 
+    elif mode == "fstab":
+        tree = test_tree()
+        tree.teardownAll()
+        fstab = test_fstab(tree)
+        #print fstab.blkidTab.devices
+        #print fstab.cryptTab.mappings
+        fmt = "%-23s %-23s %-7s %-15s %s %s"
+        for  in fstab.devices:
+            (device, mountpoint, fstype, options, dump, passno) = entry
+            print fmt % (device.fstabSpec(), mountpoint, fstype,
+                         options, dump, passno)
+
+        print
+        print "ORIGINAL:"
+        print fstab.origBuf
+        print
+    elif mode == "actions":
+        print "creating tree..."
+        tree = test_tree()
+
+        # we don't need to actually use any of the devices, so clean up now
+        tree.teardownAll()
+        print "setting up actions..."
+        tree.registerAction(ActionDestroyFormat(tree.getDeviceByName("luks-md0")))
+        tree.registerAction(ActionDestroyDevice(tree.getDeviceByName("luks-md0")))
+        fs = getFormat("ext3", device="/dev/md0", mountpoint="/opt")
+        tree.registerAction(ActionCreateFormat(tree.getDeviceByName("md0"), fs))
+        tree.registerAction(ActionDestroyFormat(tree.getDeviceByName("sda1")))
+        print "processing actions..."
+        tree.processActions(dryRun=True)
+        print "done."
+
+    tree.teardownAll()
+
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/storage/errors.py anaconda-storage/storage/errors.py
--- anaconda/storage/errors.py	1969-12-31 18:00:00.000000000 -0600
+++ anaconda-storage/storage/errors.py	2009-02-12 19:00:06.000000000 -0600
@@ -0,0 +1,46 @@
+
+class DeviceError(Exception):
+    pass
+
+class SwapError(Exception):
+    pass
+
+class SuspendError(SwapError):
+    pass
+
+class OldSwapError(SwapError):
+    pass
+
+class DeviceTreeError(Exception):
+    pass
+
+class DeviceActionError(Exception):
+    pass
+
+class PartitioningError(Exception):
+    pass
+
+class UdevError(Exception):
+    pass
+
+class DeviceFormatError(Exception):
+    pass
+
+class DMRaidMemberError(DeviceFormatError):
+    pass
+
+class FSError(DeviceFormatError):
+    pass
+
+class LUKSError(DeviceFormatError):
+    pass
+
+class MDRaidMemberError(DeviceFormatError):
+    pass
+
+class PhysicalVolumeError(DeviceFormatError):
+    pass
+
+class SwapSpaceError(DeviceFormatError):
+    pass
+
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/storage/formats/deviceformat.py anaconda-storage/storage/formats/deviceformat.py
--- anaconda/storage/formats/deviceformat.py	1969-12-31 18:00:00.000000000 -0600
+++ anaconda-storage/storage/formats/deviceformat.py	2009-02-16 12:46:02.000000000 -0600
@@ -0,0 +1,296 @@
+# deviceformat.py
+# Device format classes for anaconda's storage configuration module.
+#
+# Copyright (C) 2009  Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
+# Public License for more details.  You should have received a copy of the
+# GNU General Public License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.  Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): Dave Lehman <dlehman@redhat.com>
+#
+
+import os
+
+from errors import *
+from iutil import notify_kernel, get_sysfs_path_by_name, log_method_call
+from dm import dm_node_from_name
+
+import gettext
+_ = lambda x: gettext.ldgettext("anaconda", x)
+
+import logging
+log = logging.getLogger("storage")
+
+
+device_formats = {}
+def register_device_format(fmt_class):
+    if not issubclass(fmt_class, DeviceFormat):
+        raise ValueError("arg1 must be a subclass of DeviceFormat")
+
+    device_formats[fmt_class._type] = fmt_class
+    log.debug("registered device format class %s as %s" % (fmt_class.__name__,
+                                                           fmt_class._type))
+
+default_fstypes = ("ext4", "ext3", "ext2")
+default_boot_fstypes = ("ext3", "ext2")
+def get_default_filesystem_type(boot=None):
+    if boot:
+        fstypes = default_boot_filesystem_types
+    else:
+        fstypes = default_filesystem_types
+
+    for fstype in fstypes:
+        try:
+            supported = get_device_format_class(fstype).supported
+        except AttributeError:
+            supported = None
+        elif supported:
+            return fstype
+
+    raise DeviceFormatError("None of %s is supported by your kernel" % ",".join(fstypes))
+
+def getFormat(fmt_type, *args, **kwargs):
+    """ Return a DeviceFormat instance based on fmt_type and args.
+
+        Given a device format type and a set of constructor arguments,
+        return a DeviceFormat instance.
+
+        Return None if no suitable format class is found.
+
+        Arguments:
+
+            fmt_type -- the name of the format type (eg: 'ext3', 'swap')
+
+        Keyword Arguments:
+
+            The keyword arguments may vary according to the format type,
+            but here is the common set:
+
+            device -- path to the device on which the format resides
+            uuid -- the UUID of the (preexisting) formatted device
+            exists -- whether or not the format exists on the device            
+            
+    """
+    fmt_class = get_device_format_class(fmt_type)
+    fmt = None
+    if fmt_class:
+        fmt = fmt_class(*args, **kwargs)
+    try:
+        className = fmt.__class__.__name__
+    except AttributeError:
+        className = None
+    log.debug("getFormat('%s') returning %s instance" % (fmt_type, className))
+    return fmt
+
+def collect_device_format_classes():
+    """ Pick up all device format classes from this directory.
+
+        Note: Modules must call register_device_format(FormatClass) in
+              order for the format class to be picked up.
+    """
+    dir = os.path.dirname(__file__)
+    for module_file in os.listdir(dir):
+        if module_file.endswith(".py"):
+            mod_name = module_file[:-3]
+            try:
+                exec("import %s" % mod_name)
+            except ImportError, e:
+                log.debug("import of device format module '%s' failed" % mod_name)
+
+def get_device_format_class(fmt_type):
+    """ Return an appropriate format class based on fmt_type. """
+    if not device_formats:
+        collect_device_format_classes()
+
+    # default to no formatting
+    fmt = device_formats.get(fmt_type, DeviceFormat)
+    if not fmt:
+        for fmt_class in device_formats.values():
+            if fmt_type in fmt_class._udevTypes:
+                fmt = fmt_class
+                break
+
+    return fmt
+
+class DeviceFormat(object):
+    """ Generic device format. """
+    _type = "none"
+    _udevTypes = []
+    _formattable = False                # can be formatted
+    _supported = False                  # is supported
+    _linuxNative = False                # for clearpart
+    _packages = []                      # required packages
+    _resizable = False                  # can be resized
+    _bootable = False                   # can be used as boot 
+    _maxsize = 0                        # maximum size in MB
+    _minsize = 0                        # minimum size in MB
+    _dump = False
+    _check = False
+
+    def __init__(self, *args, **kwargs):
+        """ Create a DeviceFormat instance.
+
+            Keyword Arguments:
+
+                device -- path to the underlying device
+                uuid -- this format's UUID
+                exists -- indicates whether this is an existing format
+
+        """
+        self.device = kwargs.get("device")
+        self.uuid = kwargs.get("uuid")
+        self.exists = kwargs.get("exists")
+        self.options = kwargs.get("options")
+
+    def setDevice(self, devspec):
+        if devspec and not devspec.startswith("/"):
+            raise ValueError("device must be a fully qualified path")
+        self._device = devspec
+
+    def getDevice(self):
+        return self._device
+
+    device = property(lambda f: f.getDevice(),
+                      lambda f,d: f.setDevice(d),
+                      doc="Full path the device this format occupies")
+
+    @property
+    def name(self):
+        return self._type
+
+    @property
+    def type(self):
+        return self._type
+
+    def probe(self):
+        log_method_call(self, device=os.path.basename(self.device),
+                        type=self.type, status=self.status)
+
+    def notifyKernel(self):
+        log_method_call(self, device=os.path.basename(self.device),
+                        type=self.type)
+        if self.device.startswith("/dev/mapper/"):
+            try:
+                name = dm_node_from_name(os.path.basename(self.device))
+            except Exception, e:
+                log.warning("failed to get dm node for %s" % self.device)
+                return
+        else:
+            name = os.path.basename(self.device)
+
+
+        path = get_sysfs_path_by_name(name)
+        try:
+            notify_kernel(path, action="change")
+        except Exception, e:
+            log.warning("failed to notify kernel of change: %s" % e)
+
+
+    def create(self):
+        log_method_call(self, device=os.path.basename(self.device),
+                        type=self.type, status=self.status)
+        pass
+
+    def destroy(self):
+        log_method_call(self, device=os.path.basename(self.device),
+                        type=self.type, status=self.status)
+        # zero out the 1MB at the beginning and end of the device in the
+        # hope that it will wipe any metadata from filesystems that
+        # previously occupied this device
+        log.debug("zeroing out beginning and end of %s..." % self.device)
+        try:
+            fd = os.open(self.device, os.O_RDWR)
+            buf = '\0' * 1024 * 1024
+            os.write(fd, buf)
+            os.lseek(fd, -1024 * 1024, 2)
+            os.write(fd, buf)
+            os.close(fd)
+        except Exception, e:
+            log.error("error zeroing out %s: %s" % (self.device, e))
+
+    def setup(self):
+        log_method_call(self, device=os.path.basename(self.device),
+                        type=self.type, status=self.status)
+        pass
+
+    def teardown(self):
+        log_method_call(self, device=os.path.basename(self.device),
+                        type=self.type, status=self.status)
+        pass
+
+    @property
+    def status(self):
+        if not self.exists:
+            return False
+        return os.path.exists(self.device)
+
+    @property
+    def formattable(self):
+        """ Can we create formats of this type? """
+        return self._formattable
+
+    @property
+    def supported(self):
+        """ Is this format a supported type? """
+        return self._supported
+
+    @property
+    def packages(self):
+        """ Packages required to manage formats of this type. """
+        return self._packages
+
+    @property
+    def resizable(self):
+        """ Can formats of this type be resized? """
+        return self._resizable
+
+    @property
+    def bootable(self):
+        """ Is this format type suitable for a boot partition? """
+        return self._bootable
+
+    @property
+    def linuxNative(self):
+        """ Is this format type native to linux? """
+        return self._linuxNative
+
+    @property
+    def mountable(self):
+        """ Is this something we can mount? """
+        return False
+
+    @property
+    def dump(self):
+        """ Whether or not this format will be dumped by dump(8). """
+        return self._dump
+
+    @property
+    def check(self):
+        """ Whether or not this format is checked on boot. """
+        return self._check
+
+    @property
+    def maxsize(self):
+        """ Maximum size (in MB) for this format type. """
+        return self._maxsize
+
+    @property
+    def minsize(self):
+        """ Minimum size (in MB) for this format type. """
+        return self._minsize
+
+
+collect_device_format_classes()
+
+
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/storage/formats/dmraidmember.py anaconda-storage/storage/formats/dmraidmember.py
--- anaconda/storage/formats/dmraidmember.py	1969-12-31 18:00:00.000000000 -0600
+++ anaconda-storage/storage/formats/dmraidmember.py	2009-02-12 16:17:59.000000000 -0600
@@ -0,0 +1,88 @@
+# dmraidmember.py
+# Device format classes for anaconda's storage configuration module.
+#
+# Copyright (C) 2009  Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
+# Public License for more details.  You should have received a copy of the
+# GNU General Public License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.  Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): Dave Lehman <dlehman@redhat.com>
+#
+
+from iutil import log_method_call
+#from dm import dm_node_from_name
+from errors import *
+from deviceformat import *
+
+import gettext
+_ = lambda x: gettext.ldgettext("anaconda", x)
+
+import logging
+log = logging.getLogger("storage")
+
+
+class DMRaidMember(DeviceFormat):
+    """ A dmraid member disk. """
+    _type = "dmraidmember"
+    # XXX This looks like trouble.
+    #
+    #     Maybe a better approach is a RaidMember format with subclass
+    #     for MDRaidMember, letting all *_raid_member types fall through
+    #     to the generic RaidMember format, which is basically read-only.
+    #
+    #     One problem that presents is the possibility of someone passing
+    #     a dmraid member to the MDRaidArrayDevice constructor.
+    _udevTypes = ["adaptec_raid_member", "ddf_raid_member",
+                 "highpoint_raid_member", "isw_raid_member",
+                 "jmicron_raid_member", "lsi_mega_raid_member",
+                 "nvidia_raid_member", "promise_fasttrack_raid_member",
+                 "silicon_medley_raid_member", "via_raid_member"]
+    _formattable = False                # can be formatted
+    _supported = True                   # is supported
+    _linuxNative = False                # for clearpart
+    _packages = ["dmraid"]              # required packages
+    _resizable = False                  # can be resized
+    _bootable = False                   # can be used as boot 
+    _maxsize = 0                        # maximum size in MB
+    _minsize = 0                        # minimum size in MB
+
+    def __init__(self, *args, **kwargs):
+        """ Create a DeviceFormat instance.
+
+            Keyword Arguments:
+
+                device -- path to the underlying device
+                uuid -- this format's UUID
+                raidSet -- the name of the raidset this member belongs to
+                exists -- indicates whether this is an existing format
+
+        """
+        log_method_call(self, *args, **kwargs)
+        DeviceFormat.__init__(self, *args, **kwargs)
+        
+        self.raidSet = kwargs.get("raidSet")
+
+    def create(self):
+        log_method_call(self, device=os.path.basename(self.device),
+                        type=self.type, status=self.status)
+        raise DMRaidMemberError("creation of dmraid members is not supported")
+
+    def destroy(self):
+        log_method_call(self, device=os.path.basename(self.device),
+                        type=self.type, status=self.status)
+        raise DMRaidMemberError("destruction of dmraid members is not supported")
+
+
+register_device_format(DMRaidMember)
+
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/storage/formats/fs.py anaconda-storage/storage/formats/fs.py
--- anaconda/storage/formats/fs.py	1969-12-31 18:00:00.000000000 -0600
+++ anaconda-storage/storage/formats/fs.py	2009-02-16 12:25:56.000000000 -0600
@@ -0,0 +1,884 @@
+# filesystems.py
+# Filesystem classes for anaconda's storage configuration module.
+#
+# Copyright (C) 2009  Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
+# Public License for more details.  You should have received a copy of the
+# GNU General Public License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.  Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): Dave Lehman <dlehman@redhat.com>
+#
+
+""" Filesystem classes for use by anaconda.
+
+    TODO:
+        - migration
+        - bug 472127: allow creation of tmpfs filesystems (/tmp, /var/tmp, &c)
+"""
+import os
+import isys
+
+from errors import *
+from deviceformat import *
+import iutil
+
+import logging
+log = logging.getLogger("storage")
+
+import gettext
+_ = lambda x: gettext.ldgettext("anaconda", x)
+
+
+fs_configs = {}
+
+def get_kernel_filesystems():
+    fs_list = []
+    for line in open("/proc/filesystems").readlines():
+        fs_list.append(line.split()[-1])
+    return fs_list
+kernel_filesystems = get_kernel_filesystems()
+
+def fsFromConfig(attrs, *args, **kwargs):
+    """ Create an FS instance based on a set of attributes, passing on
+        constructor arguments.
+    """
+    # XXX NOTUSED
+    if not attrs.has_key("type"):
+        raise ValueError, _("attr dict must include a type")
+
+    fs = FS(*args, **kwargs)
+    for (attr, value) in attrs.items():
+        setattr(fs, "_%s" % attr, value)
+
+    if attrs["type"] in nodev_filesystems:
+        setattr(fs, "_nodev", True)
+
+    return fs
+
+def fsConfigFromFile(config_file):
+    """ Generate a set of attribute name/value pairs with which a
+        filesystem type can be defined.
+
+        The following config file would define a filesystem identical to
+        the static Ext3FS class definition:
+
+            type = ext3
+            mkfs = "mke2fs"
+            resizefs = "resize2fs"
+            labelfs = "e2label"
+            fsck = "e2fsck"
+            packages = ["e2fsprogs"]
+            formattable = True
+            supported = True
+            resizable = True
+            bootable = True
+            linuxNative = True
+            maxsize = 8 * 1024 * 1024
+            minsize = 0
+            defaultFormatOptions = "-t ext3"
+            defaultMountOptions = "defaults"
+
+    """
+    # XXX NOTUSED
+    lines = open(config_file).readlines()
+    fs_attrs = {}
+    for line in lines:
+        (key, value) = [t.strip() for t in line.split("=")]
+        if not hasattr(FS, "_" + key):
+            print "invalid key: %s" % key
+            continue
+
+        fs_attrs[key] = value
+
+    if not fs_attrs.has_key("type"):
+        raise ValueError, _("filesystem configuration missing a type")
+
+    # XXX what's the policy about multiple configs for a given type?
+    fs_configs[fs_attrs['type']] = fs_attrs
+
+class FS(DeviceFormat):
+    """ Filesystem class. """
+    _type = "Abstract Filesystem Class"  # fs type
+    _mkfs = ""                           # mkfs utility
+    _resizefs = ""                       # resize utility
+    _labelfs = ""                        # labeling utility
+    _fsck = ""                           # fs check utility
+    _defaultFormatOptions = []           # default options passed to mkfs
+    _defaultMountOptions = ["defaults"]  # default options passed to mount
+    _defaultLabelOptions = []
+    _defaultCheckOptions = []
+    lostAndFoundContext = None
+
+    def __init__(self, *args, **kwargs):
+        """ Create a FS instance.
+
+            Keyword Args:
+
+                device -- path to the device containing the filesystem
+                mountpoint -- the filesystem's mountpoint
+                label -- the filesystem label
+                uuid -- the filesystem UUID
+                mountopts -- mount options for the filesystem
+                size -- the filesystem's size in MiB
+                exists -- indicates whether this is an existing filesystem
+                
+        """
+        DeviceFormat.__init__(self, *args, **kwargs)
+        # TODO: fsprofiles and other ways to add format args
+        self.mountpoint = kwargs.get("mountpoint")
+        self.mountopts = kwargs.get("mountopts")
+        self.label = kwargs.get("label")
+        # filesystem size does not necessarily equal device size
+        self._size = kwargs.get("size")
+        self._targetSize = None
+        self._mountpoint = None     # the current mountpoint when mounted
+
+    def _setTargetSize(self, newsize):
+        """ Set a target size for this filesystem. """
+        if not self.exists:
+            raise FSError("filesystem has not been created")
+
+        if newsize is None:
+            # unset any outstanding resize request
+            self._targetSize = None
+            return
+
+        if not self.minsize < newsize < self.maxsize:
+            raise ValueError("invalid target size request")
+
+        self._targetSize = newsize
+
+    def _getTargetSize(self):
+        """ Get this filesystem's target size. """
+        return self._targetSize
+
+    targetSize = property(_getTargetSize, _setTargetSize,
+                          doc="Target size for this filesystem")
+
+    def _getSize(self):
+        """ Get this filesystem's size. """
+        return self._size
+
+    size = property(_getSize, doc="This filesystem's size")
+
+    def _getFormatArgs(self):
+        # FIXME: handle additional format options
+        argv = []
+        argv.extend(self.defaultFormatOptions)
+        argv.append(self.device)
+        return argv
+    
+    def format(self, progress=None):
+        """ Create the filesystem. """
+        log_method_call(self, type=self.type, device=self.device,
+                        mountpoint=self.mountpoint)
+        if self.exists:
+            raise FSError("filesystem already exists")
+
+        if not self.formattable():
+            return
+
+        if not self.mkfsProg:
+            return
+
+        if self.exists:
+            return
+
+        if not os.path.exists(self.device):
+            raise FSError("device does not exist")
+
+        argv = self._getFormatArgs()
+
+        w = None
+        if progress:
+            w = progress(_("Formatting"),
+                         _("Creating filesystem on %s...") % (self.device,),
+                         100, pulse = True)
+
+        rc = iutil.execWithPulseProgress(self.mkfsProg,
+                                         argv,
+                                         stderr="/dev/null",
+                                         progress=w)
+        if progress:
+            w.pop()
+
+        if rc:
+            raise FSError("filesystem format failed")
+                                                                  
+        self.exists = True
+        self.notifyKernel()
+
+    def _getResizeArgs(self):
+        argv = [self.device, self.targetSize]
+        return argv
+
+    def resize(self, progress=None):
+        """ Resize this filesystem to new size @newsize. """
+        if not self.exists:
+            raise FSError("filesystem has not been created")
+
+        if not self.resizable:
+            # should this instead raise an exception?
+            return
+
+        if self.targetSize is None:
+            # FIXME: implement minimum size method/attr and/or checking in
+            #        setter for targetSize property
+            return
+
+        if not self.resizefsProg:
+            return
+
+        if not os.path.exists(self.device):
+            raise FSError("device does not exist")
+
+
+        self.check(progress=progress)
+
+        argv = self._getResizeArgs()
+
+        w = None
+        if progress:
+            w = progress(_("Resizing"),
+                         _("Resizing filesystem on %s...") % (self.device,),
+                         100, pulse = True)
+
+        rc = iutil.execWithPulseProgress(self.resizefsProg,
+                                         argv,
+                                         stderr="/dev/null",
+                                         progress=w)
+        if w:
+            w.pop()
+
+        if rc:
+            raise FSError("filesystem resize failed")
+
+        # XXX must be a smarter way to do this
+        self._size = self.targetSize
+        self.notifyKernel()
+
+    def getMinimumSize(self):
+        # FIXME: this is blindly copied from fsset.py
+        if not self.exists:
+            raise FSError("filesystem has not been created")
+
+        log.warning("Unable to determinine minimal size for %s", self.device)
+        return 1
+
+    def _getCheckArgs(self):
+        argv = []
+        argv.extend(self.defaultCheckOptions)
+        argv.append(self.device)
+
+    def check(self, progress=None):
+        if not self.exists:
+            raise FSError("filesystem has not been created")
+
+        if not self.fsckProg:
+            return
+
+        if not os.path.exists(self.device):
+            raise FSError("device does not exist")
+
+        w = None
+        if progress:
+            w = progress(_("Checking"),
+                         _("Checking filesystem on %s...") %(self.device),
+                         100, pulse = True)
+
+        rc = iutil.execWithPulseProgress(self.fsckProg,
+                                         argv,
+                                         stdout="/dev/null",
+                                         stderr="/dev/null",
+                                         progress = w)
+
+        if w:
+            w.pop()
+
+        if rc >= 4:
+            raise FSError("Check of %s failed: %s" %(self.device, rc))
+
+    def mount(self, options="", chroot="/", mountpoint=None):
+        """ Mount this filesystem.
+
+            Arguments:
+
+                options: mount options (overrides all other option strings)
+                chroot: prefix to apply to mountpoint
+                mountpoint: mountpoint (overrides self.mountpoint)
+        """
+        if not self.exists:
+            raise FSError("filesystem has not been created")
+
+        if not mountpoint:
+            mountpoint = self.mountpoint
+
+        if not mountpoint:
+            raise FSError("no mountpoint given")
+
+        if self.status:
+            raise FSError("filesystem is already mounted")
+
+        if not isinstance(self, NoDevFS) and not os.path.exists(self.device):
+            raise FSError("device %s does not exist" % self.device)
+
+        # XXX os.path.join is FUBAR:
+        #
+        #         os.path.join("/mnt/foo", "/") -> "/"
+        #
+        #mountpoint = os.path.join(chroot, mountpoint)
+        mountpoint = os.path.normpath("%s/%s" % (chroot, mountpoint))
+        iutil.mkdirChain(mountpoint)
+        if flags.selinux:
+            ret = isys.resetFileContext(mountpoint)
+            log.info("set SELinux context for mountpoint %s to %s" \
+                     % (mountpoint, ret))
+
+        # passed in options override default options
+        if not options or not isinstance(options, str):
+            options = ",".join(self.defaultMountOptions))
+        
+        rc = isys.mount(self.device, mountpoint, 
+                        fstype=self.type,
+                        options=options,
+                        bindMount=isinstance(self, BindFS))
+        if rc:
+            raise FSError("mount failed")
+
+        if flags.selinux:
+            ret = isys.resetFileContext(mountpoint)
+            log.info("set SELinux context for newly mounted filesystem "
+                     "root at %s to %s" %(mountpoint, ret))
+            if FS.lostAndFountContext is None:
+                FS.lostAndFoundContext = isys.matchPathContext("/lost+found")
+            isys.setFileContext("%s/lost+found" % mountpoint,
+                                FS.lostAndFoundContext)
+
+        self._mountpoint = mountpoint
+
+    def unmount(self):
+        """ Unmount this filesystem. """
+        if not self.exists:
+            raise FSError("filesystem has not been created")
+
+        if not self._mountpoint:
+            # not mounted
+            return
+
+        if not os.path.exists(self._mountpoint):
+            raise FSError("mountpoint does not exist")
+
+        rc = isys.umount(self._mountpoint)
+        if rc:
+            raise FSError("umount failed")
+
+        self._mountpoint = None
+
+    def _getLabelArgs(self, label):
+        argv = []
+        argv.extend(self.defaultLabelOptions)
+        argv.extend([self.device, label])
+        return argv 
+
+    def writeLabel(self, label):
+        """ Create a label for this filesystem. """
+        if not self.exists:
+            raise FSError("filesystem has not been created")
+
+        if not self.labelfsProg:
+            return
+
+        if not os.path.exists(self.device):
+            raise FSError("device does not exist")
+
+        argv = self._getLabelArgs(label)
+        rc = iutil.execWithRedirect(self.labelfsProg,
+                                    argv,
+                                    stderr="/dev/null",
+                                    searchPath=1)
+        if rc:
+            raise FSError("label failed")
+
+        self.fslabel = label
+        self.notifyKernel()
+
+    @property
+    def type(self):
+        """ This filesystem's type. """
+        return self._type
+
+    @property
+    def name(self):
+        """ The filesystem type name. """
+        return self._type
+
+    @property
+    def mkfsProg(self):
+        """ Program used to create filesystems of this type. """
+        return self._mkfs
+
+    @property
+    def resizefsProg(self):
+        """ Program used to resize filesystems of this type. """
+        return self._resizefs
+
+    @property
+    def labelfsProg(self):
+        """ Program used to manage labels for this filesystem type. """
+        return self._labelfs
+
+    def supported(self):
+        # we aren't checking for fsck because we shouldn't need it
+        for prog in [self.mkfsProg, self.resizefsProg, self.labelfsProg]:
+            if not prog:
+                continue
+
+            if not filter(lambda d: os.access("%s/%s" % (d, prog), os.X_OK),
+                          os.environ["PATH"].split(":")):
+                return False
+
+        return True
+
+    @property
+    def mountable(self):
+        return self.type in kernel_filesystems
+
+    @property
+    def defaultFormatOptions(self):
+        """ Default options passed to mkfs for this filesystem type. """
+        # return a copy to prevent modification
+        return self._defaultFormatOptions[:]
+
+    @property
+    def defaultMountOptions(self):
+        """ Default options passed to mount for this filesystem type. """
+        # return a copy to prevent modification
+        return self._defaultMountOptions[:]
+
+    @property
+    def defaultLabelOptions(self):
+        """ Default options passed to labeler for this filesystem type. """
+        # return a copy to prevent modification
+        return self._defaultLabelOptions[:]
+
+    @property
+    def defaultCheckOptions(self):
+        """ Default options passed to checker for this filesystem type. """
+        # return a copy to prevent modification
+        return self._defaultCheckOptions[:]
+
+    """ These methods just wrap filesystem-specific methods in more
+        generically named methods so filesystems and formatted devices
+        like swap and LVM physical volumes can have a common API.
+    """
+    def create(self, *args, **kwargs):
+        if self.exists:
+            raise FSError("filesystem already exists")
+
+        return self.format(*args, **kwargs)
+
+    def destroy(self):
+        if not self.exists:
+            raise FSError("filesystem has not been created")
+
+        pass
+
+    def setup(self, *args, **kwargs):
+        """ Mount the filesystem.
+
+            THe filesystem will be mounted at the directory indicated by
+            self.mountpoint.
+        """
+        if not self.exists:
+            raise FSError("filesystem has not been created")
+
+        if not self.mountpoint:
+            raise FSError("cannot mount filesystem without a mountpoint")
+        #return self.mount(*args, **kwargs)
+        log.info("not mounting %s filesystem on '%s' as '%s' via setup" % (self.type, self.device, self.mountpoint))
+        pass
+
+    def teardown(self, *args, **kwargs):
+        if not self.exists:
+            raise FSError("filesystem has not been created")
+
+        return self.unmount(*args, **kwargs)
+
+    @property
+    def status(self):
+        # FIXME check /proc/mounts or similar
+        if not self.exists:
+            return False
+        return self._mountpoint is not None
+
+
+class Ext2FS(FS):
+    """ ext2 filesystem. """
+    _type = "ext2"
+    _mkfs = "mke2fs"
+    _resizefs = "resize2fs"
+    _labelfs = "e2label"
+    _fsck = "e2fsck"
+    _packages = ["e2fsprogs"]
+    _formattable = True
+    _supported = True
+    _resizable = True
+    _bootable = True
+    _linuxNative = True
+    _maxsize = 8 * 1024 * 1024
+    _minsize = 0
+    _defaultFormatOptions = []
+    _defaultMountOptions = ["defaults"]
+    _defaultCheckOptions = ["-f", "-p", "-C", "0"]
+    _dump = True
+    _check = True
+
+    def getMinimumSize(self):
+        if not self.exists:
+            raise FSError("filesystem has not been created")
+
+        if not os.path.exists(self.device):
+            raise FSError("device does not exist")
+
+        buf = iutil.execWithCapture(self.resizefsProg,
+                                    ["-P", self.device],
+                                    stderr="/dev/null")
+        minSize = None
+        for line in buf.splitlines():
+            if "minimum size of the filesystem:" not in line:
+                continue
+
+            (text, sep, minSize) = line.partition(": ")
+            minSize = int(minSize)
+
+        if minSize is None:
+            raise FSError("failed to get minimum fs size")
+
+        return minSize
+
+register_device_format(Ext2FS)
+
+
+class Ext3FS(Ext2FS):
+    """ ext3 filesystem. """
+    _type = "ext3"
+    _defaultFormatOptions = ["-t", "ext3"]
+
+register_device_format(Ext3FS)
+
+
+class Ext4FS(Ext3FS):
+    """ ext4 filesystem. """
+    _type = "ext4"
+    _bootable = False
+    _defaultFormatOptions = ["-t", "ext4"]
+
+register_device_format(Ext4FS)
+
+
+class FATFS(FS):
+    """ FAT filesystem.
+
+        XXX Do we want to subclass this for EFI or twiddle bootable based
+            on the platform?
+    """
+    _type = "vfat"
+    _mkfs = "mkdosfs"
+    _labelfs = "dosfslabel"
+    _fsck = "dosfsck"
+    _formattable = True
+    _maxsize = 1024 * 1024
+    _packages = [ "dosfstools" ]
+    _defaultMountOptions = ["umask=0077", "shortname=winnt"]
+
+    @property
+    def bootable(self):
+        retval = self._bootable
+        #if self.type in platform.bootableFSTypes:
+        #    retval = True
+
+        return retval
+
+register_device_format(FATFS)
+
+
+class BTRFS(FS):
+    """ btrfs filesystem """
+    _type = "btrfs"
+    _mkfs = "mkfs.btrfs"
+    _resizefs = "btrfsctl"
+    _formattable = True
+    _linuxNative = True
+    _bootable = False
+    _maxLabelChars = 256
+    _supported = False
+    _dump = True
+    _check = True
+    _packages = ["btrfs-progs"]
+    _maxsize = 16 * 1024 * 1024
+
+    def _getFormatArgs(self):
+        argv = []
+        argv.extend(self.defaultFormatArgs)
+        if self.fslabel:
+            argv.extend(["-L", self.fslabel])
+        argv.append(self.device)
+        return argv
+
+    def _getResizeArgs(self):
+        argv = ["-r", self.targetSize, self.device]
+        return argv
+
+register_device_format(BTRFS)
+
+
+class GFS2(FS):
+    """ gfs2 filesystem. """
+    _type = "gfs2"
+    _mkfs = "mkfs.gfs2"
+    _formattable = True
+    _defaultFormatOptions = ["-j", "1", "-p", "lock_nolock", "-O"]
+    _linuxNative = True
+    _supported = False
+    _dump = True
+    _check = True
+    _packages = ["gfs2-utils"]
+
+    @property
+    def supported(self):
+        """ Is this filesystem a supported type? """
+        supported = self._supported
+        if flags.cmdline.has_key("gfs2"):
+            supported = True
+
+        return supported
+
+register_device_format(GFS2)
+
+
+class ReiserFS(FS):
+    """ reiserfs filesystem """
+    _type = "reiserfs"
+    _mkfs = "mkreiserfs"
+    _labelfs = "reiserfstune"
+    _defaultLabelOptions = ["--label"]
+    _packages = ["reiserfs-utils"]
+    _maxsize = 8 * 1024 * 1024
+    _formattable = True
+    _linuxNative = True
+    _supported = False
+    _dump = True
+    _check = True
+
+    def format(self, progress=None):
+        """ Create the filesystem. """
+        log_method_call(self, type=self.type, device=self.device,
+                        mountpoint=self.mountpoint)
+        if self.exists:
+            raise FSError("filesystem already exists")
+
+        if not self.formattable():
+            return
+
+        if not self.mkfsProg:
+            return
+
+        if self.exists:
+            return
+
+        if not os.path.exists(self.device):
+            raise FSError("device does not exist")
+
+        argv = self._getFormatArgs()
+
+        w = None
+        if progress:
+            w = progress(_("Formatting"),
+                         _("Creating filesystem on %s...") % (self.device,),
+                         100, pulse = True)
+
+        p = os.pipe()
+        os.write(p[1], "y\n")
+        os.close(p[1])
+
+        rc = iutil.execWithPulseProgress(self.mkfsProg,
+                                         argv,
+                                         stdin=p[0],
+                                         stderr="/dev/null",
+                                         progress=w)
+        if progress:
+            w.pop()
+
+        os.close(p[0])
+
+        if rc:
+            raise FSError("filesystem format failed")
+                                                                  
+        self.exists = True
+        self.notifyKernel()
+
+register_device_format(ReiserFS)
+
+
+class JFS(FS):
+    """ JFS filesystem """
+    _type = "jfs"
+    _mkfs = "mkfs.jfs"
+    _labelfs = "jfs_tune"
+    _defaultFormatOptions = ["-q"]
+    _defaultLabelOptions = ["-L"]
+    _maxLabelChars = 16
+    _maxsize = 8 * 1024 * 1024
+    _formattable = True
+    _linuxNative = True
+    _supported = False
+    _dump = True
+    _check = True
+
+register_device_format(JFS)
+
+
+class XFS(FS):
+    """ XFS filesystem """
+    _type = "xfs"
+    _mkfs = "mkfs.xfs"
+    _labelfs = "xfs_admin"
+    _defaultFormatOptions = ["-f"]
+    _defaultLabelOptions = ["-L"]
+    _maxLabelChars = 16
+    _maxsize = 16 * 1024 * 1024
+    _formattable = True
+    _linuxNative = True
+    _supported = False
+    _dump = True
+    _check = True
+    _packages = ["xfsprogs"]
+
+register_device_format(XFS)
+
+
+class HFS(FS):
+    _type = "hfs"
+    _mkfs = "hformat"
+    _formattable = True
+
+register_device_format(HFS)
+
+
+# this doesn't need to be here
+class HFSPlus(FS):
+    _type = "hfs+"
+    _udevTypes = ["hfsplus"]
+
+register_device_format(HFSPlus)
+
+
+class NTFS(FS):
+    """ ntfs filesystem. """
+    _type = "ntfs"
+    _resizefs = "ntfsresize"
+    _resizable = True
+    _defaultMountOptions = "defaults"
+    #_packages = ["ntfsprogs"]
+
+    def getMinimumSize(self):
+        """Return the minimum filesystem size in megabytes"""
+        if not self.exists:
+            raise FSError("filesystem has not been created")
+
+        buf = iutil.execWithCapture(self.resizefsProg,
+                                    ["-m", self.device],
+                                    stderr = "/dev/tty5")
+        for l in buf.split("\n"):
+            if not l.startswith("Minsize"):
+                continue
+            try:
+                min = l.split(":")[1].strip()
+                return int(min) + 250
+            except Exception, e:
+                log.warning("Unable to parse output for minimum size on %s: %s" %(self.device, e))
+
+        log.warning("Unable to discover minimum size of filesystem on %s" %(self.device,))
+        return 1
+
+register_device_format(NTFS)
+
+
+# if this isn't going to be mountable it might as well not be here
+class NFS(FS):
+    """ NFS filesystem. """
+    _type = "nfs"
+
+    def _deviceCheck(self, devspec):
+        if not ":" in devspec:
+            raise ValueError("device must be of the form <host>:<path>")
+
+    @property
+    def mountable(self):
+        return False
+ 
+register_device_format(NFS)
+
+
+class NFSv4(NFS):
+    """ NFSv4 filesystem. """
+    _type = "nfs4"
+
+register_device_format(NFSv4)
+
+class NoDevFS(FS):
+    """ nodev filesystem base class """
+    _type = "nodev"
+
+    def __init__(self, *args, **kwargs):
+        FS.__init__(self, *args, **kwargs)
+        if self.nodev and not self.device:
+            self.device = self.type
+
+    def _deviceCheck(self, devspec):
+        pass
+
+class DevPtsFS(NoDevFS):
+    """ devpts filesystem. """
+    _type = "devpts"
+    _defaultMountOptions = ["gid=5", "mode=620"]
+
+register_device_format(DevPtsFS)
+
+
+# these don't really need to be here
+class ProcFS(NoDevFS):
+    _type = "proc"
+
+register_device_format(ProcFS)
+
+
+class SysFS(NoDevFS):
+    _type = "sysfs"
+
+register_device_format(SysFS)
+
+
+class TmpFS(NoDevFS):
+    _type = "tmpfs"
+
+register_device_format(TmpFS)
+
+
+class BindFS(FS):
+    _type = "bind"
+
+register_device_format(BindFS)
+
+
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/storage/formats/luks.py anaconda-storage/storage/formats/luks.py
--- anaconda/storage/formats/luks.py	1969-12-31 18:00:00.000000000 -0600
+++ anaconda-storage/storage/formats/luks.py	2009-02-15 13:04:52.000000000 -0600
@@ -0,0 +1,202 @@
+# luks.py
+# Device format classes for anaconda's storage configuration module.
+#
+# Copyright (C) 2009  Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
+# Public License for more details.  You should have received a copy of the
+# GNU General Public License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.  Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): Dave Lehman <dlehman@redhat.com>
+#
+
+
+
+import os
+
+from errors import *
+from iutil import log_method_call
+from deviceformat import *
+import crypto
+
+import gettext
+_ = lambda x: gettext.ldgettext("anaconda", x)
+
+import logging
+log = logging.getLogger("storage")
+
+
+class LUKS(DeviceFormat):
+    """ A LUKS device. """
+    _type = "luks"
+    _udevTypes = ["crypto_LUKS"]
+    _formattable = True                 # can be formatted
+    _supported = True                   # is supported
+    _linuxNative = True                 # for clearpart
+    _packages = ["cryptsetup-luks"]     # required packages
+
+    def __init__(self, *args, **kwargs):
+        """ Create a LUKS instance.
+
+            Keyword Arguments:
+
+                device -- the path to the underlying device
+                name -- the name of the mapped device
+                uuid -- this device's UUID
+                passphrase -- device passphrase (string)
+                key_file -- path to a file containing a key (string)
+                cipher -- cipher mode string
+                key_size -- key size in bits
+                exists -- indicates whether this is an existing format
+        """
+        log_method_call(self, *args, **kwargs)
+        DeviceFormat.__init__(self, *args, **kwargs)
+        self.cipher = kwargs.get("cipher")
+        self.key_size = kwargs.get("key_size")
+        self.mapName = kwargs.get("name")
+
+        # FIXME: these should both be lists, but managing them will be a pain
+        self.__passphrase = kwargs.get("passphrase")
+        self._key_file = kwargs.get("key_file")
+
+        if not self.mapName:
+            self.mapName = "luks-%s" % os.path.basename(self.device)
+
+    @property
+    def status(self):
+        if not self.exists:
+            return False
+        return os.path.exists("/dev/mapper/%s" % self.mapName)
+
+    def probe(self):
+        """ Probe for any missing information about this format.
+
+            cipher mode, key size
+        """
+        raise NotImplementedError("probe method not defined for LUKS")
+
+    def setup(self):
+        """ Open, or set up, the format. """
+        log_method_call(self, device=os.path.basename(self.device),
+                        type=self.type, status=self.status)
+        if not self.exists:
+            raise LUKSError("format has not been created")
+
+        if self.status:
+            return
+
+        crypto.luks_open(self.device, self.mapName,
+                       passphrase=self.__passphrase,
+                       key_file=self._key_file)
+
+        # XXX hack
+        #import udev as _udev
+        #_udev.udev_settle()
+        #del _udev
+
+    def teardown(self):
+        """ Close, or tear down, the format. """
+        log_method_call(self, device=os.path.basename(self.device),
+                        type=self.type, status=self.status)
+        if not self.exists:
+            raise LUKSError("format has not been created")
+
+        if self.status:
+            log.debug("unmapping %s" % self.mapName)
+            crypto.luks_close(self.mapName)
+
+    def create(self):
+        """ Create the format. """
+        log_method_call(self, device=os.path.basename(self.device),
+                        type=self.type, status=self.status)
+        if self.exists:
+            raise LUKSError("format already exists")
+
+        crypto.luks_format(self.device,
+                         passphrase=self.__passphrase,
+                         key_file=self._key_file,
+                         cipher=self.cipher,
+                         key_size=self.key_size)
+
+        self.uuid = crypto.luks_uuid(self.device)
+        self.notifyKernel()
+
+    @property
+    def keyFile(self):
+        """ Path to key file to be used in /etc/crypttab """
+        return self._key_file
+
+    def addKeyFromFile(self, keyfile):
+        """ Add a new key from a file.
+
+            Add the contents of the specified key file to an available key
+            slot in the LUKS header.
+        """
+        log_method_call(self, device=os.path.basename(self.device),
+                        type=self.type, status=self.status, file=keyfile)
+        if not self.exists:
+            raise LUKSError("format has not been created")
+
+        crypto.luks_add_key(self.device,
+                          passphrase=self.__passphrase,
+                          key_file=self._key_file,
+                          new_key_file=keyfile)
+
+    def addPassphrase(self, passphrase):
+        """ Add a new passphrase.
+
+            Add the specified passphrase to an available key slot in the
+            LUKS header.
+        """
+        log_method_call(self, device=os.path.basename(self.device),
+                        type=self.type, status=self.status)
+        if not self.exists:
+            raise LUKSError("format has not been created")
+
+        crypto.luks_add_key(self.device,
+                          passphrase=self.__passphrase,
+                          key_file=self._key_file,
+                          new_passphrase=passphrase)
+
+    def removeKeyFromFile(self, keyfile):
+        """ Remove a key contained in a file.
+
+            Remove key contained in the specified key file from the LUKS
+            header.
+        """
+        log_method_call(self, device=os.path.basename(self.device),
+                        type=self.type, status=self.status, file=keyfile)
+        if not self.exists:
+            raise LUKSError("format has not been created")
+
+        crypto.luks_remove_key(self.device,
+                             passphrase=self.__passphrase,
+                             key_file=self._key_file,
+                             del_key_file=keyfile)
+
+
+    def removePassphrase(self, passphrase):
+        """ Remove the specified passphrase from the LUKS header. """
+        log_method_call(self, device=os.path.basename(self.device),
+                        type=self.type, status=self.status)
+        if not self.exists:
+            raise LUKSError("format has not been created")
+
+        crypto.luks_remove_key(self.device,
+                             passphrase=self.__passphrase,
+                             key_file=self._key_file,
+                             del_passphrase=passphrase)
+
+
+register_device_format(LUKS)
+
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/storage/formats/mdraidmember.py anaconda-storage/storage/formats/mdraidmember.py
--- anaconda/storage/formats/mdraidmember.py	1969-12-31 18:00:00.000000000 -0600
+++ anaconda-storage/storage/formats/mdraidmember.py	2009-02-15 21:00:25.000000000 -0600
@@ -0,0 +1,84 @@
+# mdraidmember.py
+# Device format classes for anaconda's storage configuration module.
+#
+# Copyright (C) 2009  Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
+# Public License for more details.  You should have received a copy of the
+# GNU General Public License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.  Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): Dave Lehman <dlehman@redhat.com>
+#
+
+from errors import *
+from iutil import log_method_call
+from deviceformat import *
+import mdraid
+#from parted import PARTITION_RAID
+
+import gettext
+_ = lambda x: gettext.ldgettext("anaconda", x)
+
+import logging
+log = logging.getLogger("storage")
+
+
+class MDRaidMember(DeviceFormat):
+    """ An mdraid member disk. """
+    _type = "mdmember"
+    _udevTypes = ["linux_raid_member"]
+    #partedFlags = PARTITION_RAID
+    _formattable = True                 # can be formatted
+    _supported = True                   # is supported
+    _linuxNative = True                 # for clearpart
+    _packages = ["mdadm"]               # required packages
+    
+    def __init__(self, *args, **kwargs):
+        """ Create a MDRaidMember instance.
+
+            Keyword Arguments:
+
+                device -- path to underlying device
+                uuid -- this member device's uuid
+                mdUuid -- the uuid of the array this device belongs to
+                exists -- indicates whether this is an existing format
+
+        """
+        log_method_call(self, *args, **kwargs)
+        DeviceFormat.__init__(self, *args, **kwargs)
+        self.mdUuid = kwargs.get("mdUuid")
+        self.raidMinor = None
+
+        #self.probe()
+
+    def probe(self):
+        """ Probe for any missing information about this format. """
+        log_method_call(self, device=os.path.basename(self.device),
+                        type=self.type, status=self.status)
+        if not self.exists:
+            raise MDRaidMemberError("format has not been created")
+
+        info = mdraid.mdexamine(self.device)
+        if self.uuid is None:
+            self.uuid = info['uuid']
+        if self.raidMinor is None:
+            self.raidMinor = info['mdMinor']
+
+    @property
+    def status(self):
+        # XXX hack -- we don't have a nice way to see if the array is active
+        return False
+
+
+register_device_format(MDRaidMember)
+
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/storage/formats/physicalvolume.py anaconda-storage/storage/formats/physicalvolume.py
--- anaconda/storage/formats/physicalvolume.py	1969-12-31 18:00:00.000000000 -0600
+++ anaconda-storage/storage/formats/physicalvolume.py	2009-02-16 15:04:33.000000000 -0600
@@ -0,0 +1,116 @@
+# physicalvolume.py
+# Device format classes for anaconda's storage configuration module.
+#
+# Copyright (C) 2009  Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
+# Public License for more details.  You should have received a copy of the
+# GNU General Public License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.  Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): Dave Lehman <dlehman@redhat.com>
+#
+
+from errors import *
+from iutil import log_method_call
+from deviceformat import *
+import lvm
+#from parted import PARTITION_LVM
+
+import gettext
+_ = lambda x: gettext.ldgettext("anaconda", x)
+
+import logging
+log = logging.getLogger("storage")
+
+
+class PhysicalVolume(DeviceFormat):
+    """ An LVM physical volume. """
+    _type = "lvmpv"
+    _udevTypes = ["LVM2_member"]
+    #partedFlags = PARTITION_LVM
+    _formattable = True                 # can be formatted
+    _supported = True                   # is supported
+    _linuxNative = True                 # for clearpart
+    _packages = ["lvm2"]                # required packages
+
+    def __init__(self, *args, **kwargs):
+        """ Create a PhysicalVolume instance.
+
+            Keyword Arguments:
+
+                device -- path to the underlying device
+                uuid -- this PV's uuid (not the VG uuid)
+                vgName -- the name of the VG this PV belongs to
+                vgUuid -- the UUID of the VG this PV belongs to
+                peStart -- offset of first physical extent
+                exists -- indicates whether this is an existing format
+
+        """
+        log_method_call(self, *args, **kwargs)
+        DeviceFormat.__init__(self, *args, **kwargs)
+        self.vgName = kwargs.get("vgName")
+        self.vgUuid = kwargs.get("vgUuid")
+        # liblvm may be able to tell us this at some point, even
+        # for not-yet-created devices
+        self.peStart = kwargs.get("peStart", 0.1875)    # in MB
+
+    def probe(self):
+        """ Probe for any missing information about this device. """
+        log_method_call(self, device=os.path.basename(self.device),
+                        type=self.type, status=self.status)
+        if not self.exists:
+            raise PhysicalVolumeError("format has not been created")
+
+        pass
+        #info = lvm.pvinfo(self.device)
+        #self.vgName = info['vg_name']
+        #self.vgUuid = info['vg_uuid']
+
+    def create(self):
+        """ Create the format. """
+        log_method_call(self, device=os.path.basename(self.device),
+                        type=self.type, status=self.status)
+        if self.exists:
+            raise PhysicalVolumeError("format already exists")
+
+        """ Consider use of -Z|--zero
+            -f|--force or -y|--yes may be required
+        """
+        lvm.pvcreate(self.device)
+        self.notifyKernel()
+
+    def destroy(self):
+        """ Destroy the format. """
+        log_method_call(self, device=os.path.basename(self.device),
+                        type=self.type, status=self.status)
+        if not self.exists:
+            raise PhysicalVolumeError("format has not been created")
+
+        if self.status:
+            raise PhysicalVolumeError("device is active")
+
+        # FIXME: verify path exists?
+        lvm.pvremove(self.device)
+        self.notifyKernel()
+
+    @property
+    def status(self):
+        # XXX hack
+        for d in os.listdir("/dev/mapper"):
+            if d.startswith("%s-" % self.vgName):
+                return True
+        return False
+
+
+register_device_format(PhysicalVolume)
+
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/storage/formats/swapspace.py anaconda-storage/storage/formats/swapspace.py
--- anaconda/storage/formats/swapspace.py	1969-12-31 18:00:00.000000000 -0600
+++ anaconda-storage/storage/formats/swapspace.py	2009-02-13 13:16:20.000000000 -0600
@@ -0,0 +1,118 @@
+# swapspace.py
+# Device format classes for anaconda's storage configuration module.
+#
+# Copyright (C) 2009  Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
+# Public License for more details.  You should have received a copy of the
+# GNU General Public License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.  Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): Dave Lehman <dlehman@redhat.com>
+#
+
+import os
+
+from errors import *
+from iutil import log_method_call
+from deviceformat import *
+import swap
+#from parted import PARTITION_SWAP
+
+import gettext
+_ = lambda x: gettext.ldgettext("anaconda", x)
+
+import logging
+log = logging.getLogger("storage")
+
+
+class SwapSpace(DeviceFormat):
+    """ Swap space """
+    _type = "swap"
+    _udevTypes = ["swap"]
+    #partedFlags = PARTITION_SWAP
+    _formattable = True                # can be formatted
+    _supported = True                  # is supported
+    _linuxNative = True                # for clearpart
+
+    def __init__(self, *args, **kwargs):
+        """ Create a SwapSpace instance.
+
+            Keyword Arguments:
+
+                device -- path to the underlying device
+                uuid -- this swap space's uuid
+                label -- this swap space's label
+                priority -- this swap space's priority
+                exists -- indicates whether this is an existing format
+
+        """
+        log_method_call(self, *args, **kwargs)
+        DeviceFormat.__init__(self, *args, **kwargs)
+
+        priority = kwargs.get("priority")
+        if priority is not None:
+            if not isinstance(priority, int) or not 0 <= priority <= 32767:
+                raise ValueError("swap priority must be an integer between 0 and 32767")
+        self._priority = priority
+        self.label = kwargs.get("label")
+
+    @property
+    def options(self):
+        opts = ""
+        if self.priority is not None:
+            opts += "pri=%d" % self.priority
+
+        return opts
+
+    @property
+    def status(self):
+        """ Device status. """
+        return self.exists and swap.swapstatus(self.device)
+
+    def setup(self):
+        """ Open, or set up, a device. """
+        log_method_call(self, device=os.path.basename(self.device),
+                        type=self.type, status=self.status)
+        if not self.exists:
+            raise SwapSpaceError("format has not been created")
+
+        if self.status:
+            return
+
+        swap.swapon(self.device, priority=self.priority)
+
+    def teardown(self):
+        """ Close, or tear down, a device. """
+        log_method_call(self, device=os.path.basename(self.device),
+                        type=self.type, status=self.status)
+        if not self.exists:
+            raise SwapSpaceError("format has not been created")
+
+        if self.status:
+            swap.swapoff(self.device)
+
+    def create(self):
+        """ Create the device. """
+        log_method_call(self, device=os.path.basename(self.device),
+                        type=self.type, status=self.status)
+        if self.exists:
+            raise SwapSpaceError("format already exists")
+
+        if self.status:
+            raise SwapSpaceError("device exists and is active")
+
+        swap.mkswap(self.device, label=self.label)
+
+
+register_device_format(SwapSpace)
+
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/storage/__init__.py anaconda-storage/storage/__init__.py
--- anaconda/storage/__init__.py	1969-12-31 18:00:00.000000000 -0600
+++ anaconda-storage/storage/__init__.py	2009-02-17 13:10:54.000000000 -0600
@@ -0,0 +1,1202 @@
+# __init__.py
+# Entry point for anaconda's storage configuration module.
+#
+# Copyright (C) 2009  Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
+# Public License for more details.  You should have received a copy of the
+# GNU General Public License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.  Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): Dave Lehman <dlehman@redhat.com>
+#
+
+import os
+import time
+
+import sys
+sys.path.append("formats")
+
+if __name__ == "__main__":
+    import storage_log
+
+import iutil
+from partedUtils import getReleaseString, productMatches
+from errors import *
+from devices import *
+from deviceaction import *
+from deviceformat import getFormat
+
+import gettext
+_ = lambda x: gettext.ldgettext("anaconda", x)
+
+import logging
+log = logging.getLogger("storage")
+
+class Storage(object):
+    def __init__(self, anaconda):
+        self.anaconda = anaconda
+
+        # storage configuration variables
+        self.ignoredDisks = []
+        self.exclusiveDisks = []
+        self.doAutoPart = None
+        self.clearPartType = None
+        self.clearPartDisks = []
+        self.encryptedAutoPart = None
+        self.encryptionPassphrase = None
+        self.encryptionRetrofit = None
+        self.reinitializeDisks = None
+        self.zeroMBR = None
+        self.protectedPartitions = []
+
+        self._nextID = 0
+        self.defaultFSType = get_default_filesystem_type()
+
+        self.reset()
+
+    @property
+    def nextID(self):
+        id = self._nextID
+        self._nextID += 1
+        return id
+
+    def reset(self):
+        """ Reset storage configuration to reflect actual system state.
+
+            This should rescan from scratch but not clobber user-obtained
+            information like passphrases, iscsi config, &c
+
+        """
+        self.devicetree = DeviceTree(self.anaconda)
+        self.fsset = FSSet(self.devicetree)
+
+    @property
+    def disks(self):
+        """ A list of the disks in the device tree.
+
+            Ignored disks are not included.
+
+            This is based on the current state of the device tree and
+            does not necessarily reflect the actual on-disk state of the
+            system's disks.
+        """
+        disks = self.devicetree.getDevicesByType("disk")
+        disks.sort(key=lambda d: d.name)
+        return disks
+
+    @property
+    def partitions(self):
+        """ A list of the partitions in the device tree.
+
+            This is based on the current state of the device tree and
+            does not necessarily reflect the actual on-disk state of the
+            system's disks.
+        """
+        partitions = self.devicetree.getDevicesByType("partition")
+        partitions.sort(key=lambda d: d.name)
+        return partitions
+
+    @property
+    def vgs(self):
+        """ A list of the LVM Volume Groups in the device tree.
+
+            This is based on the current state of the device tree and
+            does not necessarily reflect the actual on-disk state of the
+            system's disks.
+        """
+        vgs = self.devicetree.getDevicesByType("lvmvg")
+        vgs.sort(key=lambda d: d.name)
+        return vgs
+
+    @property
+    def lvs(self):
+        """ A list of the LVM Logical Volumes in the device tree.
+
+            This is based on the current state of the device tree and
+            does not necessarily reflect the actual on-disk state of the
+            system's disks.
+        """
+        lvs = self.devicetree.getDevicesByType("lvmlv")
+        lvs.sort(key=lambda d: d.name)
+        return lvs
+
+    @property
+    def pvs(self):
+        """ A list of the LVM Physical Volumes in the device tree.
+
+            This is based on the current state of the device tree and
+            does not necessarily reflect the actual on-disk state of the
+            system's disks.
+        """
+        devices = self.devicetree.devices.values()
+        pvs = [d for d in devices if d.format.type == "lvmpv"]
+        pvs.sort(key=lambda d: d.name)
+        return pvs
+
+    def unusedPVs(self, vg=None):
+        unused = []
+        for pv in self.pvs:
+            used = False
+            for _vg in self.vgs:
+                if _vg.dependsOn(pv) and _vg != vg:
+                    used = True
+                    break
+                elif _vg == vg:
+                    break
+            if not used:
+                unused.append(pv)
+        return unused
+
+    @property
+    def mdarrays(self):
+        """ A list of the MD arrays in the device tree.
+
+            This is based on the current state of the device tree and
+            does not necessarily reflect the actual on-disk state of the
+            system's disks.
+        """
+        arrays = self.devicetree.getDevicesByType("mdarray")
+        arrays.sort(key=lambda d: d.name)
+        return arrays
+
+    @properties
+    def mdmembers(self):
+        """ A list of the MD member devices in the device tree.
+
+            This is based on the current state of the device tree and
+            does not necessarily reflect the actual on-disk state of the
+            system's disks.
+        """
+        devices = self.devicetree.devices.values()
+        members = [d for d in devices if d.format.type == "mdmember"]
+        members.sort(key=lambda d: d.name)
+        return members
+
+    def unusedMDMembers(self, array=None):
+        unused = []
+        for member in self.mdmembers:
+            used = False
+            for _array in self.mdarrays:
+                if _array.dependsOn(member) and _array != array:
+                    used = True
+                    break
+                elif _array == array:
+                    break
+            if not used:
+                unused.append(member)
+        return unused
+
+    def unusedMDMinors(self):
+        """ Return a list of unused minors for use in RAID. """
+        raidMinors = range(0,32)
+        for array in self.mdarrays:
+            if array.minor is not None:
+                raidMinors.remove(array.minor)
+        return raidMinors
+
+    def exceptionDisks(self):
+        """ Return a list of removable devices to save exceptions to.
+
+            FIXME: This raises the problem that the device tree can be
+                   in a state that does not reflect that actual current
+                   state of the system at any given point.
+
+                   We need a way to provide direct scanning of disks,
+                   partitions, and filesystems without relying on the
+                   larger objects' correctness.
+        """
+        dests = []
+        for device in self.devicetree.devices.values():
+            if not device.removable:
+                continue
+
+            dev = parted.Device(path=device.path)
+            disk = parted.Disk(device=dev)
+            for part in disk.partitions:
+                if part.active and \
+                   not part.getFlag(parted.PARTITION_RAID) and \
+                   not part.getFlag(parted.PARTITION_LVM) and \
+                   part.fileSystemType in ("ext3", "ext2", "fat16", "fat32"):
+                    dests.append(part.path, device.name)
+
+            if not parts:
+                dests.append(device.path, device.name)
+
+        return dests
+
+    def deviceImmutable(self, device):
+        """ Return any reason the device cannot be modified/removed.
+
+            Return False if the device can be removed.
+
+            Devices that cannot be removed include:
+
+                - protected partitions
+                - devices that are part of an md array or lvm vg
+                - extended partition containing logical partitions that
+                  meet any of the above criteria
+
+        """
+        if not isinstance(device, Device):
+            raise ValueError("arg1 (%s) must be a Device instance" % device)
+
+        if device.name in self.protectedPartitions:
+            return _("This partition is holding the data for the hard "
+                      "drive install.")
+        elif device.type == "partition" and device.isProtected:
+            # LDL formatted DASDs always have one partition, you'd have to
+            # reformat the DASD in CDL mode to get rid of it
+            return _("You cannot delete a partition of a LDL formatted "
+                     "DASD.")
+        elif device.format.type == "mdmember":
+            mdarrays = devicetree.getDevicesByType("mdarray")
+            for array in mdarrays:
+                if array.dependsOn(device):
+                    if array.minor is not None:
+                        return _("This device is part of the RAID "
+                                 "device %.") % (array.path,)
+                    else:
+                        return _("This device is part of a RAID device.")
+        elif device.format.type == "lvmpv":
+            vgs = devicetree.getDevicesByType("lvmvg")
+            for vg in vgs:
+                if vg.dependsOn(device):
+                    if vg.name is not None:
+                        return _("This device is part of the LVM "
+                                 "volume group '%s'.") % (vg.name,)
+                    else:
+                        return _("This device is part of a LVM volume "
+                                 "group.")
+        elif device.type == "partition" and device.isExtended:
+            reasons = {}
+            for dep in self.devicetree.getDependentDevices(device):
+                reason = self.deviceImmutable(dep)
+                if reason:
+                    reasons[dep.path] = reason
+            if reasons:
+                msg =  _("This device is an extended partition which "
+                         "contains logical partitions that cannot be "
+                         "deleted:\n\n")
+                for dev in reasons:
+                    msg += "%s: %s" % (dev, reasons[dev])
+                return msg
+
+        return False
+
+    def deviceDeps(self, device):
+        return self.devicetree.getDependentDevices(device)
+
+    def newPartition(self):
+        """ Return a new PartitionDevice instance ready for configuring. """
+        return PartitionDevice("req%d" % self.nextID,
+                               format=getFormat(self.defaultFSType),
+                               size=200,
+                               exists=False)
+
+    def createDevice(self, device):
+        """ Schedule creation of a device. """
+        action = ActionCreateDevice(device)
+        self.devicetree.registerAction(action)
+
+    def deleteDevice(self, device):
+        """ Schedule deletion of a device. """
+        action = ActionDestroyDevice(device)
+        self.devicetree.registerAction(action)
+
+    def formatDevice(self, device, format):
+        """ Schedule formatting of a device. """
+        self.devicetree.registerAction(ActionDestroyFormat(device))
+        self.devicetree.registerAction(ActionCreateFormat(device, format))
+
+    def formatByDefault(self, device):
+        """Return whether the device should be reformatted by default."""
+        formatlist = ['/boot', '/var', '/tmp', '/usr']
+        exceptlist = ['/home', '/usr/local', '/opt', '/var/www']
+
+        if not device.format.linuxNative:
+            return False
+
+        if device.format.mountable:
+            if device.format.mountpoint == "/" or \
+               device.format.mountpoint in formatlist:
+                return True
+
+            for p in formatlist:
+                if device.format.mountpoint.startswith(p):
+                    for q in exceptlist:
+                        if device.format.mountpoint.startswith(q):
+                            return False
+                    return True
+        elif device.format.type == "swap":
+                return True
+
+        # be safe for anything else and default to off
+        return False
+
+    def extendedPartitionsSupported(self):
+        for disk in self.disks:
+            if disk.partedDisk.supportsFeature(parted.DISK_TYPE_EXTENDED):
+                return True
+        return False
+
+ def findExistingRootDevices(anaconda, upgradeany=False):
+    """ Return a list of all root filesystems in the device tree. """
+    # XXX these imports will eventually go at the top
+    rootDevs = []
+
+    if not os.path.exists(anaconda.rootPath):
+        iutil.mkdirChain(anaconda.rootPath)
+
+    roots = []
+    for device in anaconda.id.storage.devicetree.leaves:
+        if not device.format.linuxNative or not device.format.mountable:
+            continue
+
+        # FIXME: handle the case of device already mounted on /mnt/isodir
+
+        try:
+            device.setup()
+        except Exception as e:
+            log.warning("setup of %s failed: %s" % (device.name, e))
+            continue
+
+        try:
+            device.format.mount(options="ro", mountpoint=anaconda.rootPath)
+        except Exception as e:
+            log.warning("mount of %s as %s failed: %s" % (device.name,
+                                                          device.format.type,
+                                                          e))
+            device.teardown()
+            continue
+
+        if os.access(anaconda.rootPath + "/etc/fstab", os.R_OK):
+            relstr = getReleaseString(anaconda.rootPath)
+            if upgradeany or productMatches(relstr, productName):
+                rootDevs.append((device.path, relstr))
+
+        # this handles unmounting the filesystem
+        device.teardown(recursive=True)
+
+    return rootDevs
+
+def mountExistingSystem(anaconda, rootDevice, fsset,
+                        allowDirty=None, warnDirty=None,
+                        readOnly=None):
+    """ Mount filesystems specified in rootDevice's /etc/fstab file. """
+    rootPath = anaconda.rootPath
+    if readOnly:
+        readOnly = "ro"
+    else:
+        readOnly = ""
+
+    if rootDevice.path in anaconda.id.storage.protectedPartitions and \
+       os.path.ismount("/mnt/isodir"):
+        # XXX should we add fs.BindFS?
+        isys.mount("/mnt/isodir",
+                   rootPath,
+                   fstype=rootDevice.format.type,
+                   bindMount=True)
+    else:
+        rootDevice.setup()
+        rootDevice.format.mount(chroot=rootPath,
+                                mountpoint="/",
+                                options=readOnly)
+
+    fsset.parseFSTab(chroot=rootPath)
+
+    # check for dirty filesystems
+    dirtyDevs = []
+    for device in fsset.devices:
+        if device.format.type != "ext2":
+            continue
+
+        try:
+            device.setup()
+        except DeviceError as e:
+            # we'll catch this in the main loop
+            continue
+
+        if isys.ext2IsDirty(device.path):
+            log.info("%s contains a dirty ext2 filesystem" % device.path)
+            dirtyDevs.append(device.path)
+
+    messageWindow = anaconda.intf.messageWindow
+    if not allowDirty and dirtyDevs:
+        messageWindow(_("Dirty File Systems"),
+                      _("The following file systems for your Linux system "
+                        "were not unmounted cleanly.  Please boot your "
+                        "Linux installation, let the file systems be "
+                        "checked and shut down cleanly to upgrade.\n"
+                        "%s") % "\n".join(dirtyDevs))
+        anaconda.id.storage.devicetree.teardownAll()
+        sys.exit(0)
+    elif warnDirty and dirtyDevs:
+        rc = messageWindow(_("Dirty File Systems"),
+                           _("The following file systems for your Linux "
+                             "system were not unmounted cleanly.  Would "
+                             "you like to mount them anyway?\n"
+                             "%s") % "\n".join(dirtyDevs),
+                             type = "yesno")
+        if rc == 0:
+            return -1
+
+    if flags.setupFilesystems:
+        fsset.mountFilesystems(anaconda, readOnly=readOnly, skipRoot=True)
+
+
+class BlkidTab(object):
+    """ Dictionary-like interface to blkid.tab with device path keys """
+    def __init__(self, chroot=""):
+        self.chroot = chroot
+        self.devices = {}
+
+    def parse(self):
+        path = "%s/etc/blkid/blkid.tab" % self.chroot
+        log.debug("parsing %s" % path)
+        with open(path) as f:
+            for line in f.readlines():
+                # this is pretty ugly, but an XML parser is more work than
+                # is justifiable for this purpose
+                if not line.startswith("<device "):
+                    continue
+
+                line = line[len("<device "):-len("</device>\n")]
+                (data, sep, device) = line.partition(">")
+                if not device:
+                    continue
+
+                self.devices[device] = {}
+                for pair in data.split():
+                    try:
+                        (key, value) = pair.split("=")
+                    except ValueError:
+                        continue
+
+                    self.devices[device][key] = value[1:-1] # strip off quotes
+
+    def __getitem__(self, key):
+        return self.devices[key]
+
+    def get(self, key, default=None):
+        return self.devices.get(key, default)
+
+        
+class CryptTab(object):
+    """ Dictionary-like interface to crypttab entries with map name keys """
+    def __init__(self, devTree, blkidTab=None):
+        self.devTree = devTree
+        self.blkidTab = blkidTab
+        self.chroot = chroot
+        self.mappings = {}
+
+    def parse(self, chroot=""):
+        """ Parse /etc/crypttab from an existing installation. """
+        if not chroot or not os.path.isdir(chroot):
+            chroot = ""
+
+        path = "%s/etc/crypttab" % chroot
+        log.debug("parsing %s" % path)
+        with open(path) as f:
+            for line in f.readlines():
+                if not self.blkidTab:
+                    try:
+                        self.blkidTab = BlkidTab(chroot=chroot)
+                        self.blkidTab.parse()
+                    except Exception:
+                        self.blkidTab = None
+
+                for line in lines:
+                    (line, pound, comment) = line.partition("#")
+                    fields = line.split()
+                    if not 2 <= len(fields) <= 4:
+                        continue
+                    elif len(fields) == 2:
+                        fields.extend(['none', ''])
+                    elif len(fields) == 3:
+                        fields.append('')
+
+                    (name, devspec, keyfile, options) = fields
+
+                    # resolve devspec to a device in the tree
+                    device = resolveDevice(self.devTree,
+                                           devspec,
+                                           blkidTab=self.blkidTab)
+                    if device:
+                        self.mappings[name] = {"device": device,
+                                               "keyfile": keyfile,
+                                               "options": options}
+
+    def populate(self):
+        """ Populate the instance based on the device tree's contents. """
+        for device in self.devTree:
+            # XXX should we put them all in there or just the ones that
+            #     are part of a device containing swap or a filesystem?
+            #
+            #       Put them all in here -- we can filter from FSSet
+            if device.format.type != "luks":
+                continue
+
+            key_file = device.format.keyFile
+            if not key_file:
+                key_file = "none"
+
+            options = device.format.options
+            if not options:
+                options = ""
+
+            self.mappings[device.format.mapName] = {"device": device,
+                                                    "keyfile": key_file,
+                                                    "options": options}
+
+    def crypttab(self):
+        """ Write out /etc/crypttab """
+        crypttab = ""
+        for name in self.mappings:
+            entry = self[name]
+            crypttab += "%s UUID=%s %s %s" % (name,
+                                              entry['device'].format.uuid,
+                                              entry['keyfile'],
+                                              entry['options'])
+        return crypttab                       
+
+    def __getitem__(self, key):
+        return self.mappings[key]
+
+    def get(self, key, default=None):
+        return self.mappings.get(key, default)
+
+def fstab_entry_get_field(entry, fieldname):
+    index = fstab_fields[fieldname]
+    return entry[index]
+
+def get_containing_device(path, devicetree):
+    """ Return the device that a path resides on. """
+    if not os.path.exists(path):
+        return None
+
+    st = os.stat(path)
+    major = os.major(st.st_dev)
+    minor = os.minor(st.st_dev)
+    link = "/sys/dev/block/%s:%s" % (major, minor)
+    if not os.path.exists(link):
+        return None
+
+    try:
+        device_name = os.path.basename(os.readlink(link))
+    except Exception:
+        return None
+
+    return devicetree.getDeviceByName(device_name)
+
+
+class FSSet(object):
+    """ A class to represent a set of filesystems. """
+    def __init__(self, devTree):
+        self.devices = []
+        self.devTree = devTree
+        self.cryptTab = None
+        self.blkidTab = None
+
+    def parseFSTab(self, chroot=""):
+        """ parse /etc/fstab
+
+            preconditions:
+                all storage devices have been scanned, including filesystems
+            postconditions:
+
+            FIXME: control which exceptions we raise
+
+            XXX do we care about bind mounts?
+                how about nodev mounts?
+                loop mounts?
+                nfs?
+        """
+        if not chroot or not os.path.isdir(chroot):
+            chroot = ""
+
+        path = "%s/etc/fstab" % chroot
+        if not os.access(path, os.R_OK):
+            # XXX should we raise an exception instead?
+            log.info("cannot open %s for read" % path)
+            return
+
+        blkidTab = BlkidTab(chroot=chroot)
+        try:
+            blkidTab.parse()
+            log.debug("blkid.tab devs: %s" % blkidTab.devices.keys())
+        except Exception as e:
+            log.info("error parsing blkid.tab: %s" % e)
+            blkidTab = None
+
+        cryptTab = CryptTab(self.devTree, blkidTab=blkidTab)
+        try:
+            cryptTab.parse(chroot=chroot)
+            log.debug("crypttab maps: %s" % cryptTab.mappings.keys())
+        except Exception as e:
+            log.info("error parsing crypttab: %s" % e)
+            cryptTab = None
+
+        self.blkidTab = blkidTab
+        self.cryptTab = cryptTab
+
+        with open(path) as f:
+            log.debug("parsing %s" % path)
+
+            lines = f.readlines()
+
+            # save the original file
+            self.origFStab = ''.join(lines)
+
+            for line in lines:
+                # strip off comments
+                (line, pound, comment) = line.partition("#")
+                fields = line.split()
+
+                if not 4 <= len(fields) <= 6:
+                    continue
+                elif len(fields) == 4:
+                    fields.extend([0, 0])
+                elif len(fields) == 5:
+                    fields.append(0)
+
+                (devspec, mountpoint, fstype, options, dump, passno) = fields
+
+                # find device in the tree
+                device = resolveDevice(self.devTree,
+                                       devspec,
+                                       cryptTab=cryptTab,
+                                       blkidTab=blkidTab)
+                if device:
+                    # fall through to the bottom of this block
+                    pass
+                elif devspec.startswith("/dev/loop"):
+                    # FIXME: WTF do we do with these?
+                elif ":" in devspec:
+                    # NFS -- preserve but otherwise ignore?
+                    fs = getFormat(fstype)
+                    device = NFSDevice(devspec,
+                                       format=getFormat(fstype,
+                                                        device=devspec),
+                                       exists=True)
+                elif devspec.startswith("/") and fstype == "swap":
+                    # swap file?
+                    device = FileDevice(devspec,
+                                        parents=get_containing_device(devspec),
+                                        exists=True)
+                elif fstype == "bind" or "bind" in options:
+                    # bind mount... set fstype so later comparison won't
+                    # turn up false positives
+                    fstype = "bind"
+                    device = FileDevice(devspec,
+                                        parents=get_containing_device(devspec),
+                                        exists=True)
+                    device.setFormat(getFormat("bind",
+                                               device=device.path,
+                                               exists=True))
+                else:
+                    # nodev filesystem -- preserve or drop completely?
+                    fs = getFormat(fstype)
+                    if fs.nodev:
+                        device = NoDevice(fs)
+                    #self.devTree._addDevice(device)
+
+                if device is None:
+                    log.error("failed to resolve %s (%s) from fstab" % (devspec,
+                                                                        fstype))
+                    continue
+
+                # make sure, if we're using a device from the tree, that
+                # the device's format we found matches what's in the fstab
+                fmt = getFormat(fstype, device=device.path)
+                if fmt.type != device.format.type:
+                    log.warning("scanned format (%s) differs from fstab "
+                                "format (%s)" % (device.format.type, fstype))
+
+                if device.format.mountable:
+                    device.format.mountpoint = mountpoint
+                    device.format.mountopts = options
+
+                # is this useful?
+                try:
+                    device.format.options = options
+                except AttributeError:
+                    pass
+
+                self.devices.append(device)
+
+    def fsFreeSpace(self, chroot='/'):
+        space = []
+        for device in self.devices:
+            if not device.format.mountable or \
+               not device.format.status:
+                continue
+
+            path = "%s/%s" % (chroot, device.format.mountpoint)
+            try:
+                space.append((device.format.mountpoint,
+                              isys.pathSpaceAvailable(path)))
+            except SystemError:
+                log.error("failed to calculate free space for %s" % (device.format.mountpoint,))
+
+        space.sort(key=lambda s: s[1])
+        return space
+
+    def mtab(self):
+        format = "%s %s %s %s 0 0\n"
+        mtab = ""
+        for device in self.devices:
+            if not device.format.status:
+                continue
+            if not device.format.mountable:
+                continue
+            if device.format.mountpoint:
+                options = device.format.mountopts
+                options = options.replace("defaults,", "")
+                options = options.replace("defaults", "")
+                if options:
+                    options = "rw," + options
+                else:
+                    options = "rw"
+                mtab = mtab + format % (device.path,
+                                        device.format.mountpoint,
+                                        device.format.type,
+                                        options)
+        return mtab
+
+    def turnOnSwap(self, intf=None, upgrading=None):
+        for device in self.swapDevices:
+            try:
+                device.setup()
+                device.setupFormat()
+            except SuspendError:
+                if intf:
+                    if upgrading:
+                        msg = _("The swap device:\n\n     %s\n\n"
+                                "in your /etc/fstab file is currently in "
+                                "use as a software suspend device, "
+                                "which means your system is hibernating. "
+                                "To perform an upgrade, please shut down "
+                                "your system rather than hibernating it.") \
+                              % device.path)
+                    else:
+                        msg = _("The swap device:\n\n     %s\n\n"
+                                "in your /etc/fstab file is currently in "
+                                "use as a software suspend device, "
+                                "which means your system is hibernating. "
+                                "If you are performing a new install, "
+                                "make sure the installer is set "
+                                "to format all swap devices.") \
+                              % device.path)
+
+                    intf.messageWindow(_("Error"), msg)
+                sys.exit(0)
+            except DeviceError as msg:
+                if intf:
+                    if upgrading:
+                        err = _("Error enabling swap device %s: %s\n\n"
+                                "The /etc/fstab on your upgrade partition "
+                                "does not reference a valid swap "
+                                "device.\n\nPress OK to exit the "
+                                "installer") % (device.path, msg)
+                    else:
+                        err = _("Error enabling swap device %s: %s\n\n"
+                                "This most likely means this swap "
+                                "device has not been initialized.\n\n"
+                                "Press OK to exit the installer.") % \
+                              (device.path, msg)
+                    intf.messageWindow(_("Error"), err)
+                sys.exit(0)
+
+    def mountFilesystems(self, anaconda, raiseErrors=None, readOnly=None,
+                         skipRoot=False):
+        intf = anaconda.intf
+        for device in [d for d in self.devices if d.isleaf]:
+            if not device.format.mountable:
+                continue
+
+            if skipRoot and mountpoint == "/":
+                continue
+
+            if "noauto" in options.split(","):
+                continue
+
+            try:
+                device.setup()
+            except Exception as msg:
+                # FIXME: need an error popup
+                continue
+
+            if readOnly:
+                options = "%s,%s" % (options, readOnly)
+
+            try:
+                device.format.mount(options=options, chroot=anaconda.rootPath)
+            except OSError as (num, msg):
+                if intf:
+                    if num == errno.EEXIST:
+                        intf.messageWindow(_("Invalid mount point"),
+                                           _("An error occurred when trying "
+                                             "to create %s.  Some element of "
+                                             "this path is not a directory. "
+                                             "This is a fatal error and the "
+                                             "install cannot continue.\n\n"
+                                             "Press <Enter> to exit the "
+                                             "installer.") % (mountpoint,))
+                    else:
+                        intf.messageWindow(_("Invalid mount point"),
+                                           _("An error occurred when trying "
+                                             "to create %s: %s.  This is "
+                                             "a fatal error and the install "
+                                             "cannot continue.\n\n"
+                                             "Press <Enter> to exit the "
+                                             "installer.") % (mountpoint,
+                                                              msg))
+                log.error("OSError: (%d) %s" % (num, msg) )
+                sys.exit(0)
+            except SystemError as (num, msg):
+                if raiseErrors:
+                    raise
+                if intf and not device.format.linuxNative:
+                    ret = intf.messageWindow(_("Unable to mount filesystem"),
+                                             _("An error occurred mounting "
+                                             "device %s as %s.  You may "
+                                             "continue installation, but "
+                                             "there may be problems.") %
+                                             (device.path,
+                                              mountpoint),
+                                             type="custom",
+                                             custom_icon="warning",
+                                             custom_buttons=[_("_Exit installer"),
+                                                            _("_Continue")])
+
+                    if ret == 0:
+                        sys.exit(0)
+                    else:
+                        continue
+
+                log.error("SystemError: (%d) %s" % (num, msg) )
+                sys.exit(0)
+            except FSError as msg:
+                if intf:
+                    intf.messageWindow(_("Unable to mount filesystem"),
+                                       _("An error occurred mounting "
+                                         "device %s as %s: %s. This is "
+                                         "a fatal error and the install "
+                                         "cannot continue.\n\n"
+                                         "Press <Enter> to exit the "
+                                         "installer.") % (mountpoint,
+                                                          msg))
+                log.error("FSError: %s" % msg)
+                sys.exit(0)
+
+    def umountFilesystems(self, instPath, ignoreErrors=True, swapoff=True):
+        # XXX if we tracked the /dev bind mount this wouln't be necessary
+        if os.path.ismount("%s/dev" % instPath):
+            isys.umount("%s/dev" % instPath, removeDir=0)
+
+        # reverse works in place so we take a slice
+        devices = self.devices[:]
+        devices.reverse()
+        for device in [d for d in devices if d.isleaf]:
+            if not device.format.mountable and \
+               (device.format.type != "swap" or swapoff):
+                continue
+
+            device.teardownFormat()
+            device.teardown()
+
+    def createSwapFile(self, device, rootPath, size):
+        """ Create and activate a swap file under rootPath. """
+        filename = "/SWAP"
+        count = 0   
+        while os.path.exists("%s/%s" % (rootPath, filename) or \
+              self.devTree.getDeviceByName(filename):
+            file = os.path.normpath("%s/%s" % (rootPath, filename))
+            count += 1
+            filename = "/SWAP-%d" % count
+
+        dev = FileDevice(filename,
+                         size=size,
+                         parents=self.rootDevice,
+                         format=getFormat("swap", device=filename))
+        dev.create()
+        dev.createFormat()
+        dev.setup()
+        dev.setupFormat()
+        # nasty, nasty
+        self.devTree._addDevice(dev)
+        self.devices.append(dev)
+
+    def mkDevRoot(self, instPath):
+        root = self.rootDevice
+        dev = "%s/dev/%s" % (instPath, root.device.getDevice())
+        if not os.path.exists("%s/dev/root" %(instPath,)) and os.path.exists(dev):
+            rdev = os.stat(dev).st_rdev
+            os.mknod("%s/dev/root" % (instPath,), stat.S_IFBLK | 0600, rdev)
+
+    @property
+    def swapDevices(self):
+        swaps = []
+        for device in self.devices:
+            if device.format.type == "swap":
+                swaps.append(device)
+        return swaps
+
+    @property
+    def rootDevice(self):
+        for device in self.devices:
+            try:
+                mountpoint = device.format.mountpoint
+            except AttributeError:
+                mountpoint = None
+
+            if mountpoint == "/":
+                return device
+
+    @property
+    def migratableDevices(self):
+        """ Return a list of devices whose filesystems can be migrated. """
+        return []   # no migrate for you!!!
+
+    def write(self, chroot="/"):
+        """ write out all config files based on the set of filesystems """
+        pass
+
+    def crypttab(self):
+        # if we are upgrading, do we want to mess w/ crypttab?
+        # gut reaction says no, but plymouth needs the names to be very
+        # specific for passphrase prompting
+        if not self.cryptTab:
+            self.cryptTab = CryptTab(self.devTree)
+            self.cryptTab.populate()
+
+        # prune crypttab -- only mappings required by one or more entries
+        for name in self.cryptTab.mappings:
+            keep = False
+            mapInfo = self.cryptTab[name]
+            cryptoDev = mapInfo['device']
+            for device in self.devices:
+                if device.dependsOn(cryptoDev):
+                    keep = True
+                    break
+
+            if not keep:
+                del self.cryptTab.mappings[name]
+
+        return self.cryptTab.crypttab()
+
+    def mdadmConf(self):
+        """ Return the contents of mdadm.conf. """
+        arrays = self.devTree.getDevicesByType("mdarray")
+        conf = ""
+        for array in arrays:
+            writeConf = False
+            if array in self.devices:
+                writeConf = True
+            else:
+                for device in self.devices:
+                    if device.dependsOn(array):
+                        writeConf = True
+                        break
+
+            if writeConf:
+                conf += array.mdadmConfEntry
+
+        return conf
+
+    def writeFSTab(self, chroot="/"):
+        """ Write out /etc/fstab. """
+        pass
+
+    def fstab (self):
+        format = "%-23s %-23s %-7s %-15s %d %d\n"
+        fstab = """
+#
+# /etc/fstab
+# Created by anaconda on %s
+#
+# Accessible filesystems, by reference, are maintained under '/dev/disk'
+# See man pages fstab(5), findfs(8), mount(8) and/or vol_id(8) for more info
+#
+""" % time.asctime()
+
+        for device in self.device:
+            # why the hell do we put swap in the fstab, anyway?
+            if not device.format.mountable and device.format.type != "swap":
+                continue
+
+            fstype = device.format.type
+            if fstype == "swap":
+                mountpoint = "swap"
+                options = device.format.options
+            else:
+                mountpoint = device.format.mountpoint
+                options = gettattr(device.format.mountopts
+                if not mountpoint:
+                    log.warning("%s filesystem on %s has no mountpoint" % \
+                                                            (fstype,
+                                                             device.path))
+                    continue
+
+            if not options:
+                # why not, right?
+                options = "defaults"
+
+            devspec = device.fstabSpec
+            dump = device.format.dump
+            if device.format.check and mountpoint == "/":
+                passno = 1
+            elif device.format.check:
+                passno = 2
+            else:
+                passno = 0
+            fstab = fstab + device.fstabComment
+            fstab = fstab + format % (devspec, mountpoint, fstype,
+                                      options, dump, passno)
+        return fstab
+
+    @property
+    def bootDevice(self):
+        mntDict = {}
+        bootDev = None
+        for device in [d for d in self.devices if d.format.mountable]:
+            mntDict[device.format.mountpoint] = device
+
+        # FIXME: this ppc stuff feels kind of crufty -- the abstraction
+        # here needs a little bit of work
+        if iutil.getPPCMacGen() == "NewWorld":
+            for device in self.devTree.devices.values():
+                # XXX do we need to also check the size?
+                if device.format.type == "hfs" and device.bootable:
+                    bootDev = device
+        elif (iutil.getPPCMachine() == "pSeries" or
+              iutil.getPPCMachine() == "iSeries"):
+            # we want the first prep partition or the first newly formatted one
+            # XXX who cares if PReP is formatted? it has no format.
+            partitions = self.devTree.getDeviceByType("partition")
+            for device in partitions:
+                if device.partedPartition.getFlag(parted.PARTITION_PREP) and \
+                   (bestprep is None or \
+                    (bestprep.format.exists and not device.format.exists)):
+                    bootDev = device
+        elif iutil.isEfi():
+            bootDev = mntDict.get('/boot/efi')
+        else:
+            bootDev = mntDict.get("/boot", mntDict.get("/"))
+
+        # XXX should we raise an error if no boot device was found?
+        return bootDev
+
+    def bootloaderChoices(self, bl):
+        ret = {}
+        bootDev = self.bootDevice
+
+        if bootDev is None:
+            log.warning("no boot device set")
+            return ret
+
+        if iutil.isEfi():
+            ret['boot'] = (bootDev.path, N_("EFI System Partition"))
+        elif bootDev.device.getName() == "RAIDDevice":
+            ret['boot'] = (bootDev.path, N_("RAID Device"))
+            ret['mbr'] = (bl.drivelist[0], N_("Master Boot Record (MBR)"))
+        elif iutil.getPPCMacGen() == "NewWorld":
+            ret['boot'] = (bootDev.path, N_("Apple Bootstrap"))
+            partitions = self.devTree.getDevicesByType("partition")
+            for (n, device) in enumerate(partitions):
+                if (device.format.type == "hfs" and device.bootable and \
+                    device.path != bootDev.path)):
+                    ret['boot%d' %(n,)] = (device.path,
+                                           N_("Apple Bootstrap"))
+        elif (iutil.getPPCMachine() == "pSeries" or
+              iutil.getPPCMachine() == "iSeries"):
+            ret['boot'] = (bootDev.device, N_("PPC PReP Boot"))
+        else:
+            ret['boot'] = (bootDev.device,
+                           N_("First sector of boot partition"))
+            ret['mbr'] = (bl.drivelist[0], N_("Master Boot Record (MBR)"))
+
+        return ret
+
+           
+def resolveDevice(tree, devspec, blkidTab=None, cryptTab=None):
+    # find device in the tree
+    device = None
+    if devspec.startswith("UUID="):
+        # device-by-uuid
+        uuid = devspec.partition("=")[2]
+        device = tree.uuids.get(uuid)
+        if device is None:
+            log.error("failed to resolve device %s" % devspec)
+    elif devspec.startswith("LABEL="):
+        # device-by-label
+        label = devspec.partition("=")[2]
+        device = tree.fslabels.get(label)
+        if device is None:
+            log.error("failed to resolve device %s" % devspec)
+    elif devspec.startswith("/dev/"):
+        # device path
+        device = tree.devices.get(devspec)
+        if device is None:
+            if blkidTab:
+                # try to use the blkid.tab to correlate the device
+                # path with a UUID
+                blkidTabEnt = blkidTab.get(devspec)
+                if blkidTabEnt:
+                    log.debug("found blkid.tab entry for '%s'" % devspec)
+                    uuid = blkidTabEnt.get("UUID")
+                    if uuid:
+                        device = tree.getDeviceByUuid(uuid)
+                        if device:
+                            devstr = device.name
+                        else:
+                            devstr = "None"
+                        log.debug("found device '%s' in tree" % devstr)
+                    if device and device.format and \
+                       device.format.type == "luks":
+                        map_name = device.format.mapName
+                        log.debug("luks device; map name is '%s'" % map_name)
+                        mapped_dev = tree.getDeviceByName(map_name)
+                        if mapped_dev:
+                            device = mapped_dev
+
+            if device is None and cryptTab and \
+               devspec.startswith("/dev/mapper/"):
+                # try to use a dm-crypt mapping name to 
+                # obtain the underlying device, possibly
+                # using blkid.tab
+                cryptTabEnt = cryptTab.get(devspec.split("/")[-1])
+                if cryptTabEnt:
+                    luks_dev = cryptTabEnt['device']
+                    try:
+                        device = tree.getChildren(luks_dev)[0]
+                    except IndexError as e:
+                        pass
+            elif device is None:
+                # dear lvm: can we please have a few more device nodes
+                #           for each logical volume?
+                #           three just doesn't seem like enough.
+                name = devspec[5:]      # strip off leading "/dev/"
+                (vg_name, slash, lv_name) = name.partition("/")
+                if lv_name and not "/" in lv_name:
+                    # looks like we may have one
+                    lv = "%s-%s" % (vg_name, lv_name)
+                    device = tree.getDeviceByName(lv)
+
+    if device:
+        log.debug("resolved '%s' to '%s' (%s)" % (devspec, device.name, device.type))
+    else:
+        log.debug("failed to resolve '%s'" % devspec)
+    return device
+
+
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/storage/miscutils.py anaconda-storage/storage/miscutils.py
--- anaconda/storage/miscutils.py	1969-12-31 18:00:00.000000000 -0600
+++ anaconda-storage/storage/miscutils.py	2009-02-12 16:25:10.000000000 -0600
@@ -0,0 +1,58 @@
+# iutil.py stubs
+import sys
+import os
+
+import logging
+log = logging.getLogger("storage")
+
+def notify_kernel(path, action="change"):
+    """ Signal the kernel that the specified device has changed. """
+    log.debug("notifying kernel of '%s' event on device %s" % (action, path))
+    path = os.path.join(path, "uevent")
+    if not path.startswith("/sys/") or not os.access(path, os.W_OK):
+        log.debug("sysfs path '%s' invalid" % path)
+        raise ValueError("invalid sysfs path")
+
+    f = open(path, "a")
+    f.write("%s\n" % action)
+    f.close()
+
+def get_sysfs_path_by_name(dev_name, class_name="block"):
+    dev_name = os.path.basename(dev_name)
+    sysfs_class_dir = "/sys/class/%s" % class_name
+    dev_path = os.path.join(sysfs_class_dir, dev_name)
+    if os.path.exists(dev_path):
+        return dev_path
+
+import inspect
+def log_method_call(d, *args, **kwargs):
+    classname = d.__class__.__name__
+    methodname = inspect.stack()[1][3]
+    fmt = "%s.%s:"
+    fmt_args = [classname, methodname]
+    for arg in args:
+        fmt += " %s ;"
+        fmt_args.append(arg)
+
+    for k, v in kwargs.items():
+        fmt += " %s: %s ;"
+        fmt_args.extend([k, v])
+
+    log.debug(fmt % tuple(fmt_args))
+
+def numeric_type(num):
+    """ Verify that a value is given as a numeric data type.
+
+        Return the number if the type is sensible or raise ValueError
+        if not.
+    """
+    if num is None:
+        num = 0
+    elif not (isinstance(num, int) or \
+              isinstance(num, long) or \
+              isinstance(num, float)):
+        raise ValueError("value (%s) must be either a number or None" % num)
+
+    return num
+
+
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/storage/partitioning.py anaconda-storage/storage/partitioning.py
--- anaconda/storage/partitioning.py	1969-12-31 18:00:00.000000000 -0600
+++ anaconda-storage/storage/partitioning.py	2009-02-12 16:17:59.000000000 -0600
@@ -0,0 +1,743 @@
+# partitioning.py
+# Disk partitioning functions.
+#
+# Copyright (C) 2009  Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
+# Public License for more details.  You should have received a copy of the
+# GNU General Public License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.  Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): Dave Lehman <dlehman@redhat.com>
+#
+
+import os
+import copy
+from operator import add, sub
+
+# XXX temporary
+import sys
+sys.path.insert(0, "/root/pyparted/src")
+sys.path.insert(1, "/root/pyparted/src/.libs")
+import parted
+
+from errors import *
+from deviceaction import *
+
+import gettext
+_ = lambda x: gettext.ldgettext("anaconda", x)
+
+import logging
+log = logging.getLogger("storage")
+
+
+def clearPartitions(deviceTree, clearPartType=None, clearPartDisks=[]):
+    """ Clear partitions and dependent devices from disks.
+
+        Arguments:
+
+            deviceTree -- a DeviceTree instance
+
+        Keyword arguments:
+
+            clearPartType -- a string, one of "none", "all", or "linux"
+            clearPartDisks -- a list of basenames of disks to consider
+
+        NOTES:
+
+            - Needs some error handling, especially for the parted bits.
+            - Should use device actions instead of dev.destroy, &c
+
+    """
+    if not clearPartType or clearPartType == "none":
+        # not much to do -- just remove any empty extended partitions
+        removeEmptyExtendedPartitions(deviceTree)
+        return
+
+    # we are only interested in partitions that physically exist
+    partitions = deviceTree.getDevicesByType("partition")
+    partitions = [p for p in partitions if p.exists]
+    disks = []  # a list of disks from which we've removed partitions
+
+    for part in partitions:
+        log.debug("clearpart: looking at %s" % part.name)
+        clear = False   # whether or not we will clear this partition
+
+        # if we got a list of disks to clear, make sure this one's on it
+        if clearPartDisks and not part.disk.name in clearPartDisks:
+            continue
+
+        # we don't want to fool with extended partitions, freespace, &c
+        if part.partType not in (parted.PARTITION_NORMAL,
+                                 parted.PARTITION_LOGICAL):
+            continue
+
+        if clearPartType == "all":
+            clear = True
+        else:
+            if part.format and part.format.linuxNative:
+                clear = True
+            elif part.partedPartition.getFlag(parted.PARTITION_LVM) or \
+                 part.partedPartition.getFlag(parted.PARTITION_RAID) or \
+                 part.partedPartition.getFlag(parted.PARTITION_SWAP):
+                clear = True
+
+        # TODO: do platform-specific checks on ia64, pSeries, iSeries, mac
+
+        if not clear:
+            continue
+
+        log.debug("clearing %s" % part.name)
+
+        # XXX is there any argument for not removing incomplete devices?
+        #       -- maybe some RAID devices
+        devices = deviceTree.getDependentDevices(part)
+        while devices:
+            log.debug("devices to remove: %s" % ([d.name for d in devices],))
+            leaves = [d for d in devices if d.isleaf]
+            log.debug("leaves to remove: %s" % ([d.name for d in leaves],))
+            for leaf in leaves:
+                # FIXME: use DeviceAction
+                action = ActionDestroyDevice(leaf)
+                deviceTree.registerAction(action)
+                #leaf.destroyFormat()
+                #leaf.teardown()
+                #leaf.destroy()
+                #deviceTree.removeDevice(leaf)
+                devices.remove(leaf)
+
+        # XXX can/should this be moved into PartitionDevice?
+        part.partedPartition.disk.removePartition(part.partedPartition)
+        log.debug("partitions: %s" % [p.getDeviceNodeName() for p in part.partedPartition.disk.partitions])
+        disk_name = os.path.basename(part.partedPartition.disk.device.path)
+        if disk_name not in disks:
+            disks.append(disk_name)
+
+        action = ActionDestroyDevice(part)
+        deviceTree.registerAction(action)
+        #part.destroyFormat()
+        #part.teardown()
+        #part.destroy()
+        #deviceTree.removeDevice(part)
+
+    # now remove any empty extended partitions
+    removeEmptyExtendedPartitions(deviceTree)
+
+
+def removeEmptyExtendedPartitions(tree):
+    disks = tree.getDevicesByType("disk")
+    for disk in disks:
+        log.debug("checking whether disk %s has an empty extended" % disk.name)
+        extended = disk.partedDisk.getExtendedPartition()
+        logical_parts = disk.partedDisk.getLogicalPartitions()
+        log.debug("extended is %s ; logicals is %s" % (extended, [p.getDeviceNodeName() for p in logical_parts]))
+        if extended and not logical_parts:
+            log.debug("removing empty extended partition from %s" % disk.name)
+            extended_name = extended.getDeviceNodeName()
+            extended = tree.getDeviceByName(extended_name)
+            tree.registerAction(ActionDestroyDevice(extended))
+            disk.partedDisk.removePartition(extended.partedPartition)
+
+
+def partitionCompare(part1, part2):
+    """ More specifically defined partitions come first.
+
+        < 1 => x < y
+          0 => x == y
+        > 1 => x > y
+    """
+    ret = 0
+
+    # bootable partitions to the front
+    ret -= cmp(part1.req_bootable, part2.req_bootable) * 1000
+
+    # more specific disk specs to the front of the list
+    ret += cmp(len(part1.parents), len(part2.parents)) * 500
+
+    # primary-only to the front of the list
+    ret -= cmp(part1.req_primary, part2.req_primary) * 200
+
+    # larger requests go to the front of the list
+    ret -= cmp(part1.size, part2.size) * 100
+
+    # fixed size requests to the front
+    ret += cmp(part1.req_grow, part2.req_grow) * 50
+
+    # potentially larger growable requests go to the front
+    if part1.req_grow and part2.req_grow:
+        if not part1.req_max_size and part2.req_max_size:
+            ret -= 25
+        elif part1.req_max_size and not part2.req_max_size:
+            ret += 25
+        else:
+            ret -= cmp(part1.req_max_size, part2.req_max_size) * 25
+
+    if ret > 0:
+        ret = 1
+    elif ret < 0:
+        ret = -1
+
+    return ret
+
+def getNextPartitionType(disk, no_primary=None):
+    """ Find the type of partition to create next on a disk.
+
+        Return a parted partition type value representing the type of the
+        next partition we will create on this disk.
+
+        If there is only one free primary partition and we can create an
+        extended partition, we do that.
+
+        If there are free primary slots and an extended partition we will
+        recommend creating a primary partition. This can be overridden
+        with the keyword argument no_primary.
+
+        Arguments:
+
+            disk -- a parted.Disk instance representing the disk
+
+        Keyword arguments:
+
+            no_primary -- given a choice between primary and logical
+                          partitions, prefer logical
+
+    """
+    part_type = None
+    extended = disk.getExtendedPartition()
+    supports_extended = disk.supportsFeature(parted.DISK_TYPE_EXTENDED)
+    logical_count = len(disk.getLogicalPartitions())
+    max_logicals = disk.getMaxLogicalPartitions()
+    primary_count = disk.primaryPartitionCount
+
+    if primary_count == disk.maxPrimaryPartitionCount and \
+       extended and logical_count < max_logicals:
+        part_type = parted.PARTITION_LOGICAL
+    elif primary_count == (disk.maxPrimaryPartitionCount - 1) and \
+         not extended and supports_extended:
+        # last chance to create an extended partition
+        part_type = parted.PARTITION_EXTENDED
+    elif no_primary and extended and logical_count < max_logicals:
+        # create a logical even though we could presumably create a
+        # primary instead
+        part_type = parted.PARTITION_LOGICAL
+    elif not no_primary:
+        # XXX there is a possiblity that the only remaining free space on
+        #     the disk lies within the extended partition, but we will
+        #     try to create a primary first
+        part_type = parted.PARTITION_NORMAL
+
+    return part_type
+
+def getBestFreeSpaceRegion(disk, part_type, req_size,
+                           boot=None, best_free=None):
+    """ Return the "best" free region on the specified disk.
+
+        For non-boot partitions, we return the largest free region on the
+        disk. For boot partitions, we return the first region that is
+        large enough to hold the partition.
+
+        Partition type (parted's PARTITION_NORMAL, PARTITION_LOGICAL) is
+        taken into account when locating a suitable free region.
+
+        For locating the best region from among several disks, the keyword
+        argument best_free allows the specification of a current "best"
+        free region with which to compare the best from this disk. The
+        overall best region is returned.
+
+        Arguments:
+
+            disk -- the disk (a parted.Disk instance)
+            part_type -- the type of partition we want to allocate
+                         (one of parted's partition type constants)
+            req_size -- the requested size of the partition (in MB)
+
+        Keyword arguments:
+
+            boot -- indicates whether this will be a bootable partition
+                    (boolean)
+            best_free -- current best free region for this partition
+
+    """
+    log.debug("getBestFreeSpaceRegion: disk=%s part_type=%d req_size=%dMB boot=%s best=%s" % (disk.device.path, part_type, req_size, boot, best_free))
+    extended = disk.getExtendedPartition()
+    for _range in disk.getFreeSpaceRegions():
+        if extended:
+            # find out if there is any overlap between this region and the
+            # extended partition
+            log.debug("looking for intersection between extended (%d-%d) and free (%d-%d)" % (extended.geometry.start, extended.geometry.end, _range.start, _range.end))
+
+            # parted.Geometry.overlapsWith can handle this
+            try:
+                free_geom = extended.geometry.intersect(_range)
+            except ArithmeticError, e:
+                # this freespace region does not lie within the extended
+                # partition's geometry
+                free_geom = None
+
+            if (free_geom and part_type == parted.PARTITION_NORMAL) or \
+               (not free_geom and part_type == parted.PARTITION_LOGICAL):
+                log.debug("free region not suitable for request")
+                continue
+
+            if part_type == parted.PARTITION_NORMAL:
+                # we're allocating a primary and the region is not within
+                # the extended, so we use the original region
+                free_geom = _range
+        else:
+            free_geom = _range
+
+        log.debug("current free range is %d-%d (%dMB)" % (free_geom.start,
+                                                          free_geom.end,
+                                                          free_geom.getSize()))
+        free_size = free_geom.getSize()
+
+        if req_size <= free_size:
+            if not best_free or free_geom.length > best_free.length:
+                best_free = free_geom
+
+                if boot:
+                    # if this is a bootable partition we want to
+                    # use the first freespace region large enough
+                    # to satisfy the request
+                    break
+
+    return best_free
+
+def doPartitioning(deviceTree, use_disks=[]):
+    """ Allocate and grow partitions.
+
+        FIXME: need a way to specify that a subset of disks be used
+
+        Arguments:
+
+            deviceTree -- the device tree (a DeviceTree instance)
+
+        Keyword arguments:
+
+            use_disks -- a list of basenames of disks to use
+
+    """
+    #removeEmptyExtendedPartitions(deviceTree)
+
+    disks = deviceTree.getDevicesByType("disk")
+    partitions = deviceTree.getDevicesByType("partition")
+
+    if use_disks:
+        # only use specified disks
+        disks = [d for d in disks if d.name in use_disks]
+
+    # FIXME: make sure non-existent partitions have empty parents list
+    allocatePartitions(disks, partitions)
+    growPartitions(disks, partitions)
+
+    # XXX hack -- if we created any extended partitions we need to add
+    #             them to the tree now
+    for disk in disks:
+        extended = disk.partedDisk.getExtendedPartition()
+        if deviceTree.getDeviceByName(extended.getDeviceNodeName()):
+            # this extended partition is preexisting
+            continue
+
+        device = PartitionDevice(extended.getDeviceNodeName(),
+                                 parents=disk)
+        device.setPartedPartition(extended)
+        device.disk = disk
+
+def allocatePartitions(disks, partitions):
+    """ Allocate partitions based on requested features.
+
+        Non-existing partitions are sorted according to their requested
+        attributes, and then allocated.
+
+        The basic approach to sorting is that the more specifically-
+        defined a request is, the earlier it will be allocated. See
+        the function partitionCompare for details on the sorting
+        criteria.
+
+        The PartitionDevice instances will have their name and parents
+        attributes set once they have been allocated.
+    """
+    log.debug("allocatePartitions: disks=%s ; partitions=%s" % (disks,
+                                                                partitions))
+    new_partitions = [p for p in partitions if not p.exists]
+    new_partitions.sort(cmp=partitionCompare)
+
+    # XXX is this needed anymore?
+    partedDisks = {}
+    for disk in disks:
+        if disk.path not in partedDisks.keys():
+            partedDisks[disk.path] = disk.partedDisk #.duplicate()
+
+    # remove all newly added partitions from the disk
+    log.debug("removing all non-preexisting from disk(s)")
+    for _part in new_partitions:
+        if _part.partedPartition:
+            #_part.disk.partedDisk.removePartition(_part.partedPartition)
+            partedDisk = partedDisks[_part.disk.partedDisk.device.path]
+            #log.debug("removing part %s (%s) from disk %s (%s)" % (_part.partedPartition.path, [p.path for p in _part.partedPartition.disk.partitions], partedDisk.device.path, [p.path for p in partedDisk.partitions]))
+            partedDisk.removePartition(_part.partedPartition)
+            # remove empty extended so it doesn't interfere
+            extended = partedDisk.getExtendedPartition()
+            if extended and not partedDisk.getLogicalPartitions():
+                log.debug("removing empty extended partition")
+                #partedDisk.minimizeExtendedPartition()
+                partedDisk.removePartition(extended)
+
+    for _part in new_partitions:
+        # obtain the set of candidate disks
+        req_disks = []
+        if _part.disk:
+            # we have a already selected a disk for this request
+            req_disks = [_part.disk]
+        elif _part.req_disks:
+            # use the requested disk set
+            req_disks = _part.req_disks
+        else:
+            # no disks specified means any disk will do
+            req_disks = disks
+
+        log.debug("allocating partition: %s ; disks: %s ; boot: %s ; primary: %s ; size: %dMB ; grow: %s ; max_size: %s" % (_part.name, req_disks, _part.req_bootable, _part.req_primary, _part.req_size, _part.req_grow, _part.req_max_size))
+        free = None
+        # loop through disks
+        for _disk in req_disks:
+            disk = partedDisks[_disk.path]
+            #for p in disk.partitions:
+            #    log.debug("disk %s: part %s" % (disk.device.path, p.path))
+            sectorSize = disk.device.physicalSectorSize
+            part_type = parted.PARTITION_NORMAL
+            best = None
+
+            # TODO: On alpha we are supposed to reserve either one or two
+            #       MB at the beginning of each disk. Awesome.
+            #         -- maybe we do not care about alpha...
+
+            log.debug("checking freespace on %s" % _disk.name)
+
+            part_type = getNextPartitionType(disk)
+            if part_type is None:
+                # can't allocate any more partitions on this disk
+                log.debug("no free partition slots on %s" % _disk.name)
+                continue
+
+            if _part.req_primary and part_type != parted.PARTITION_NORMAL:
+                # we need a primary slot and none are free on this disk
+                log.debug("no primary slots available on %s" % _disk.name)
+                continue
+
+            best = getBestFreeSpaceRegion(disk,
+                                          part_type,
+                                          _part.req_size,
+                                          best_free=free,
+                                          boot=_part.req_bootable)
+            
+            if best == free and not _part.req_primary and \
+               part_type == parted.PARTITION_NORMAL:
+                # see if we can do better with a logical partition
+                log.debug("not enough free space for primary -- trying logical")
+                part_type = getNextPartitionType(disk, no_primary=True)
+                if part_type:
+                    free = getBestFreeSpaceRegion(disk,
+                                                  part_type,
+                                                  _part.req_size,
+                                                  best_free=free,
+                                                  boot=_part.req_bootable)
+            else:
+                free = best
+
+            if free and _part.req_bootable:
+                # if this is a bootable partition we want to
+                # use the first freespace region large enough
+                # to satisfy the request
+                log.debug("found free space for bootable request")
+                break
+
+        if free is None:
+            raise PartitioningError("not enough free space on disks")
+
+        # create the extended partition if needed
+        # TODO: move to a function (disk, free)
+        if part_type == parted.PARTITION_EXTENDED:
+            log.debug("creating extended partition")
+            geometry = parted.Geometry(device=disk.device,
+                                       start=free.start,
+                                       length=free.length,
+                                       end=free.end)
+            extended = parted.Partition(disk=disk,
+                                        type=parted.PARTITION_EXTENDED,
+                                        geometry=geometry)
+            constraint = parted.Constraint(device=disk.device)
+            # FIXME: we should add this to the tree as well
+            disk.addPartition(extended, constraint)
+
+            # end proposed function
+
+            # now the extended partition exists, so set type to logical
+            part_type = parted.PARTITION_LOGICAL
+
+            # recalculate freespace
+            log.debug("recalculating free space")
+            free = getBestFreeSpaceRegion(disk,
+                                          part_type,
+                                          _part.req_size,
+                                          boot=_part.req_bootable)
+            if not free:
+                raise PartitioningError("not enough free space after "
+                                        "creating extended partition")
+
+        # create minimum geometry for this request
+        # req_size is in MB
+        length = (_part.req_size * (1024 * 1024)) / sectorSize
+        new_geom = parted.Geometry(device=disk.device,
+                                   start=free.start,
+                                   length=length)
+
+        # create the partition and add it to the disk
+        partition = parted.Partition(disk=disk,
+                                     type=part_type,
+                                     geometry=new_geom)
+        disk.addPartition(partition=partition,
+                          constraint=disk.device.getConstraint())
+#                          constraint=parted.Constraint(device=disk.device))
+        log.debug("created partition %s of %dMB and added it to %s" % (partition.getDeviceNodeName(), partition.getSize(), disk))
+        _part.setPartedPartition(partition)
+        _part.disk = _disk
+
+
+def growPartitions(disks, partitions):
+    """ Grow all growable partition requests.
+
+        All requests should know what disk they will be on by the time
+        this function is called. This is reflected in the
+        PartitionDevice's disk attribute. Note that the req_disks
+        attribute remains unchanged.
+
+        The total available free space is summed up for each disk and
+        partition requests are allocated a maximum percentage of the
+        available free space on their disk based on their own base size.
+
+        Each attempted size means calling allocatePartitions again with
+        one request's size having changed.
+
+        After taking into account several factors that may limit the
+        maximum size of a requested partition, we arrive at a firm
+        maximum number of sectors by which a request can potentially grow.
+
+        An initial attempt is made to allocate the full maximum size. If
+        this fails, we begin a rough binary search with a maximum of three
+        iterations to settle on a new size.
+
+        TODO: Call disk.maximizePartition for each growable partition that
+              has not been allocated its full share of the free space upon
+              termination of each disk's loop iteration. Any detected
+              maximum size can be specified via a parted Constraint.
+
+        Arguments:
+
+            disks -- a list of all usable disks (DiskDevice instances)
+            partitions -- a list of all partitions (PartitionDevice
+                          instances)
+    """
+    log.debug("growPartitions: disks=%s, partitions=%s" % ([d.name for d in disks], [p.name for p in partitions]))
+    all_growable = [p for p in partitions if p.req_grow]
+
+    # sort requests by base size in decreasing order
+    all_growable.sort(key=lambda p: p.req_size, reverse=True)
+
+    log.debug("growable requests are %s" % [p.name for p in all_growable])
+
+    for disk in disks:
+        log.debug("growing requests on %s" % disk.name)
+        sectorSize = disk.partedDisk.device.physicalSectorSize
+        # get a list of free space regions on the disk
+        free = disk.partedDisk.getFreeSpaceRegions()
+        # sort the free regions in decreasing order of size
+        free.sort(key=lambda r: r.length, reverse=True)
+        disk_free = reduce(lambda x,y: x + y, [f.length for f in free])
+
+        # make a list of partitions currently allocated on this disk
+        # -- they're already sorted
+        growable = []
+        disk_total = 0
+        for part in all_growable:
+            #log.debug("checking if part %s (%s) is on this disk" % (part.name,
+            #                                                        part.disk.name))
+            if part.disk == disk:
+                growable.append(part)
+                disk_total += (part.req_size * (1024 * 1024)) / sectorSize
+
+        # now we loop through the partitions...
+        for part in growable:
+            # calculate max number of sectors this request can grow
+            sectors = (part.req_size * (1024 * 1024)) / sectorSize
+            share = float(sectors) / float(disk_total)
+            max_grow = share * disk_free
+            max_mb = (max_grow * sectorSize) / (1024 * 1024)
+            log.debug("%s: base_size=%dMB, max_size=%sMB" % (part.name,
+                                                             part.req_base_size,
+                                                             part.req_max_size))
+            log.debug("%s: %dMB (%d sectors, or %d%% of %d)" % (part.name,
+                                                                max_mb,
+                                                                max_grow,
+                                                                share * 100,
+                                                                disk_free))
+
+            log.debug("checking constraints on max size...")
+            # don't grow beyond the request's maximum size
+            if part.req_max_size:
+                log.debug("max_size: %dMB" % part.req_max_size)
+                # FIXME: round down to nearest cylinder boundary
+                max_sect = (part.req_max_size * (1024 * 1024)) / sectorSize
+                if max_sect < sectors + max_grow:
+                    max_grow = (max_sect - sectors)
+
+            # don't grow beyond the resident filesystem's max size
+            if part.format and getattr(part.format, 'maxsize', 0):
+                log.debug("format maxsize: %dMB" % part.format.maxsize)
+                # FIXME: round down to nearest cylinder boundary
+                max_sect = (part.format.maxsize * (1024 * 1024)) / sectorSize
+                if max_sect < sectors + max_grow:
+                    max_grow = (max_sect - sectors)
+
+            # we can only grow as much as the largest free region on the disk
+            if free[0].length < max_grow:
+                log.debug("largest free region: %d sectors (%dMB)" % (free[0].length, free[0].getSize()))
+                # FIXME: round down to nearest cylinder boundary
+                max_grow = free[0].length
+
+            # Now, we try to grow this partition as close to max_grow
+            # sectors as we can.
+            #
+            # We could call allocatePartitions after modifying this
+            # request and saving the original value of part.req_size,
+            # or we could try to use disk.maximizePartition().
+            req_sectors = (part.req_size * 1024 * 1024) / sectorSize
+            max_sectors = req_sectors + max_grow
+            max_size = (max_sectors * sectorSize) / (1024 * 1024)
+            orig_size = part.req_size
+            # try the max size to begin with
+            log.debug("attempting to allocate maximum size: %dMB" % max_size)
+            part.req_size = max_size
+            try:
+                allocatePartitions(disks, partitions)
+            except PartitioningError, e:
+                log.debug("max size attempt failed: %s (%dMB)" % (part.name,
+                                                                  max_size))
+                part.req_size = orig_size
+            else:
+                continue
+
+            log.debug("starting binary search: size=%d max_size=%d" % (part.req_size, max_size))
+            count = 0
+            op_func = add
+            increment = max_grow
+            last_good_size = part.req_size
+            last_outcome = None
+            while part.req_size < max_size and count < 3:
+                last_size = part.req_size
+                increment /= 2
+                sectors = op_func(sectors, increment)
+                part.req_size = (sectors * sectorSize) / (1024 * 1024)
+                log.debug("attempting size=%dMB" % part.req_size)
+                count += 1
+                try:
+                    allocatePartitions(disks, partitions)
+                except PartitioningError, e:
+                    log.debug("attempt at %dMB failed" % part.req_size)
+                    op_func = sub
+                    last_outcome = False
+                else:
+                    last_good_size = part.req_size
+                    last_outcome = True
+
+            if not last_outcome:
+                part.req_size = last_good_size
+                log.debug("backing up to size=%dMB" % part.req_size)
+                try:
+                    allocatePartitions(disks, partitions)
+                except PartitioningError, e:
+                    raise PartitioningError("failed to grow partitions")
+
+            # TODO: call disk.maximizePartition with max_size as the
+            #       constraint, in case it can grab some more free space
+
+
+    # reset all requests to their original requested size
+    for part in partitions:
+        if part.exists:
+            continue
+        part.req_size = part.req_base_size
+
+def growLVM(tree):
+    """ Grow LVs according to the sizes of the PVs. """
+    vgs = tree.getDevicesByType("lvm vg")
+    for vg in vgs:
+        total_free = vg.freeSpace
+        if not total_free:
+            log.debug("vg %s has no free space" % vg.name)
+            continue
+
+        # figure out how much to grow each LV
+        grow_amounts = {}
+        lv_total = vg.size - total_free
+
+        # This first loop is to calculate percentage-based growth
+        # amounts. These are based on total free space.
+        for lv in lvs:
+            if not lv.req_grow or not lv.req_percent:
+                continue
+
+            portion = (lv_req_percent * 0.01)
+            # clamp growth amount to a multiple of vg extent size
+            grow = vg.align(portion * vg.vgFree)
+            new_size = lv.req_size + grow
+            if lv.req_max_size and new_size > lv.req_max_size:
+                # clamp growth amount to a multiple of vg extent size
+                grow -= align(new_size - lv.req_max_size)
+
+            # clamp growth amount to a multiple of vg extent size
+            grow_amounts[lv.name] = vg.align(grow)
+            total_free -= grow
+
+        # This second loop is to calculate non-percentage-based growth
+        # amounts. These are based on free space remaining after
+        # calculating percentage-based growth amounts.
+        for lv in lvs:
+            if not lv.req_grow or lv.req_percent:
+                continue
+
+            portion = float(lv.req_size) / float(lv_total)
+            # clamp growth amount to a multiple of vg extent size
+            grow = vg.align(portion * total_free)
+            new_size = lv.req_size + grow
+            if lv.req_max_size and new_size > lv.req_max_size:
+                # clamp growth amount to a multiple of vg extent size
+                grow -= vg.align(new_size - lv.req_max_size)
+
+            grow_amounts[lv.name] = grow
+
+        if not grow_amounts:
+            log.debug("no growable lvs in vg %s" % vg.name)
+            continue
+
+        # now grow the lvs by the amounts we've calculated above
+        for lv in lvs:
+            if lv.name not in grow_amounts.keys():
+                continue
+            lv.size = new_size
+
+        # now there shouldn't be any free space left, but if there is we
+        # should allocate it to one of the LVs
+        vg_free = vg.freeSpace
+        log.debug("vg %s still has %dMB free" % (vg.name, vg_free))
+
+
+
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/storage/STATUS anaconda-storage/storage/STATUS
--- anaconda/storage/STATUS	1969-12-31 18:00:00.000000000 -0600
+++ anaconda-storage/storage/STATUS	2009-02-13 00:15:44.000000000 -0600
@@ -0,0 +1,53 @@
+Fri Feb 13 00:04:17 CST 2009
+
+Moved all code with knowledge of the Device/DeviceTree/DeviceFormat
+classes back into DeviceTree (addUdevDevice). Made little functions
+in udev.py to grab specific pieces of device information. Added lots of
+type assurances throughout the udev functions.
+
+I think I finally got LVM Device/DeviceFormat classes sorted out as far
+as space calculations.
+
+Began to merge into anaconda (in storage/), including a new layout.
+
+Sorted out most things with upgrade, rescue.
+
+FSTab class has turned into FSSet. Not sure how I feel about it. I need
+to decide how that's going to be. It might be the biggest remaining part
+of the merge.
+
+After FSSet is sorted out it will be time to tackle fleshing out
+DeviceTree (or whatever will replace Partitions), doAutoPart, and
+integration with the UI. After that, who knows...
+
+Tue Feb 10 22:34:01 CST 2009
+
+Autopart is working pretty well with one disk, although I'm hitting some
+segfaults when clearing empty extended partitions. I have had success
+with and without preexisting partitions. Growth amounts appear to be
+correct, except that I don't think I have covered the case of base size
+being zero with grow set. The case where we create an extended partition
+during autopart is also not handled gracefully since we do not have access
+to the device tree inside the autopart functions.
+
+LV/VG growing is well on its way, but I need to flesh out the VG, LV
+device classes before I can test it.
+
+Top items on TODO list are lvm, merge w/ anaconda, then dmraid, mpath.
+
+Fri Feb  6 19:47:58 CST 2009
+
+Clearpart seems to be working well, including removal of dependent devices
+like VGs, LVs, and md arrays.
+
+Populating the tree seems to work well both for running systems and from
+scratch. There are some hitches with udev knowing what's changed, though.
+It might be time to reconnect the notify_kernel function so it sends
+uevents again.
+
+Some basic tests have been successful: creating and destroying both lvm
+and md devices, creating and removing various device formats, and
+clearing of partitions and their dependent devices.
+
+Next thing to do is partitioning...
+
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/storage/storage_log.py anaconda-storage/storage/storage_log.py
--- anaconda/storage/storage_log.py	1969-12-31 18:00:00.000000000 -0600
+++ anaconda-storage/storage/storage_log.py	2009-02-12 16:17:59.000000000 -0600
@@ -0,0 +1,13 @@
+import logging
+
+
+#handler = logging.StreamHandler()
+handler = logging.FileHandler("/tmp/storage.log")
+formatter = logging.Formatter("[%(asctime)s] %(levelname)8s: %(message)s")
+handler.setFormatter(formatter)
+
+logger = logging.getLogger("storage")
+logger.addHandler(handler)
+logger.setLevel(logging.DEBUG)
+
+
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/storage/udev.py anaconda-storage/storage/udev.py
--- anaconda/storage/udev.py	1969-12-31 18:00:00.000000000 -0600
+++ anaconda-storage/storage/udev.py	2009-02-12 16:17:59.000000000 -0600
@@ -0,0 +1,273 @@
+# udev.py
+# Python module for querying the udev database for device information.
+#
+# Copyright (C) 2009  Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
+# Public License for more details.  You should have received a copy of the
+# GNU General Public License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.  Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): Dave Lehman <dlehman@redhat.com>
+#
+
+import os
+import re
+
+import iutil
+
+import logging
+log = logging.getLogger("storage")
+
+
+def udev_get_block_devices():
+    udev_settle(timeout=2)
+    entries = []
+    for path in enumerate_block_devices():
+        entry = udev_get_block_device(path)
+        if entry:
+            entries.append(entry)
+    return entries
+
+def enumerate_block_devices():
+    top_dir = "/sys/class/block"
+    devices = []
+    for dev_name in os.listdir(top_dir):
+        if dev_name.startswith("loop") or dev_name.startswith("ram"):
+            continue
+        full_path = os.path.join(top_dir, dev_name)
+        link_ref = os.readlink(full_path)
+        real_path = os.path.join(top_dir, link_ref)
+        sysfs_path = os.path.normpath(real_path)
+        devices.append(sysfs_path)
+    return devices
+
+def udev_get_block_device(sysfs_path):
+    log.debug("getting udev info for %s" % sysfs_path)
+    if not os.path.exists(sysfs_path):
+        log.debug("%s does not exist" % sysfs_path)
+        return None
+
+    db_entry = sysfs_path[4:].replace("/", "\\x2f")
+    db_root = "/dev/.udev/db"
+    db_path = os.path.normpath("%s/%s" % (db_root, db_entry))
+    if not os.access(db_path, os.R_OK):
+        log.debug("db entry %s does not exist" % db_path)
+        return None
+
+    entry = open(db_path).read()
+    dev = udev_parse_block_entry(entry)
+    if dev:
+        # XXX why do we do this? is /sys going to move during installation?
+        dev['sysfs_path'] = sysfs_path[4:]  # strip off the leading '/sys'
+        dev = udev_parse_uevent_file(dev)
+
+    # now add in the contents of the uevent file since they're handy
+    return dev
+
+def udev_parse_uevent_file(dev):
+    path = os.path.normpath("/sys/%s/uevent" % dev['sysfs_path'])
+    if not os.access(path, os.R_OK):
+        return dev
+
+    with open(path) as f:
+        for line in f.readlines():
+            (key, equals, value) = line.strip().partition("=")
+            if not equals:
+                continue
+
+            dev[key] = value
+
+    return dev
+
+def udev_parse_block_entry(buf):
+    dev = {'name': None,
+           'symlinks': []}
+
+    for line in buf.splitlines():
+        line.strip()
+        (tag, sep, val) = line.partition(":")
+        if not sep:
+            continue
+
+        if tag == "N":
+            dev['name'] = val
+        elif tag == "S":
+            dev['symlinks'].append(val)
+        elif tag == "E":
+            if val.count("=") > 1 and val.count(" ") == 1:
+                # eg: LVM2_LV_NAME when querying the VG for its LVs
+                vars = val.split()
+                vals = []
+                var_name = None
+                for (index, subval) in enumerate(vars):
+                    (var_name, sep, var_val) = subval.partition("=")
+                    if sep:
+                        vals.append(var_val)
+
+                dev[var_name] = vals
+            else:
+                (var_name, sep, var_val) = val.partition("=")
+                if not sep:
+                    continue
+
+                if var_val.count(" "):
+                    # eg: DEVLINKS
+                    var_val = var_val.split()
+
+                dev[var_name] = var_val
+
+    if dev.get("name"):
+        return dev
+
+def udev_settle(timeout=None):
+    argv = ["settle"]
+    if timeout:
+        argv.append("--timeout=%d" % int(timeout))
+
+    iutil.execWithRedirect("udevadm", argv, stderr="/dev/null", searchPath=1)
+
+def udev_trigger(subsystem=None):
+    argv = ["trigger"]
+    if subsystem:
+        argv.append("--subsystem-match=%s" % subsystem)
+
+    iutil.execWithRedirect("udevadm", argv, stderr="/dev/null", searchPath=1)
+
+
+""" These are functions for retrieving specific pieces of information from
+    udev database entries.
+"""
+def udev_device_get_name(udev_info):
+    """ Return the best name for a device based on the udev db data. """
+    return udev_info.get("DM_NAME", udev_info["name"])
+
+def udev_device_get_format(udev_info):
+    """ Return a device's format type as reported by udev. """
+    return udev_info.get("ID_FS_TYPE")
+
+def udev_device_get_uuid(udev_info):
+    """ Get the UUID from the device's format as reported by udev. """
+    md_uuid = udev_info.get("MD_UUID")
+    uuid = udev_info.get("ID_FS_UUID")
+    # we don't want to return the array's uuid as a member's uuid
+    if uuid and not md_uuid == uuid:
+        return udev_info.get("ID_FS_UUID")
+
+def udev_device_get_label(udev_info):
+    """ Get the label from the device's format as reported by udev. """
+    return udev_info.get("ID_FS_LABEL")
+
+def udev_device_is_dm(info):
+    """ Return True if the device is a device-mapper device. """
+    return info.has_key("DM_NAME")
+
+def udev_device_is_md(info):
+    """ Return True is the device is an mdraid array device. """
+    return info.has_key("MD_METADATA")
+
+def udev_device_is_cdrom(info):
+    """ Return True if the device is an optical drive. """
+    # FIXME: how can we differentiate USB drives from CD-ROM drives?
+    #         -- USB drives also generate a sdX device.
+    return info.get("ID_CDROM") == "1"
+
+def udev_device_is_disk(info):
+    """ Return True is the device is a disk. """
+    has_range = os.path.exists("/sys/%s/range" % info['sysfs_path'])
+    return info.get("DEVTYPE") == "disk" or has_range
+
+def udev_device_is_partition(info):
+    has_start = os.path.exists("/sys/%s/start" % info['sysfs_path'])
+    return info.get("DEVTYPE") == "partition" or has_start
+
+def udev_device_get_sysfs_path(info):
+    return info['sysfs_path']
+
+def udev_device_get_major(info):
+    return int(info["MAJOR"])
+
+def udev_device_get_minor(info):
+    return int(info["MINOR"])
+
+def udev_device_get_md_level(info):
+    return info["MD_LEVEL"]
+
+def udev_device_get_md_devices(info):
+    return int(info["MD_DEVICES"])
+
+def udev_device_get_md_uuid(info):
+    return info["MD_UUID"]
+
+def udev_device_get_vg_name(info):
+    return info['LVM2_VG_NAME']
+
+def udev_device_get_vg_uuid(info):
+    return info['LVM2_VG_UUID']
+
+def udev_device_get_vg_size(info):
+    # lvm's decmial precision is not configurable, so we tell it to use
+    # KB and convert to MB here
+    return float(info['LVM2_VG_SIZE']) / 1024
+
+def udev_device_get_vg_free(info):
+    # lvm's decmial precision is not configurable, so we tell it to use
+    # KB and convert to MB here
+    return float(info['LVM2_VG_FREE']) / 1024
+
+def udev_device_get_vg_extent_size(info):
+    # lvm's decmial precision is not configurable, so we tell it to use
+    # KB and convert to MB here
+    return float(info['LVM2_VG_EXTENT_SIZE']) / 1024
+
+def udev_device_get_vg_extent_count(info):
+    return int(info['LVM2_VG_EXTENT_COUNT'])
+
+def udev_device_get_vg_free_extents(info):
+    return int(info['LVM2_VG_FREE_COUNT'])
+
+def udev_device_get_vg_pv_count(info):
+    return int(info['LVM2_PV_COUNT'])
+
+def udev_device_get_pv_pe_start(info):
+    # lvm's decmial precision is not configurable, so we tell it to use
+    # KB and convert to MB here
+    return float(info['LVM2_PE_START']) / 1024
+
+def udev_device_get_lv_names(info):
+    names = info['LVM2_LV_NAME']
+    if not names:
+        names = []
+    elif not isinstance(names, list):
+        names = [names]
+    return names
+
+def udev_device_get_lv_uuids(info):
+    uuids = info['LVM2_LV_UUID']
+    if not uuids:
+        uuids = []
+    elif not isinstance(uuids, list):
+        uuids = [uuids]
+    return uuids
+
+def udev_device_get_lv_sizes(info):
+    # lvm's decmial precision is not configurable, so we tell it to use
+    # KB and convert to MB here
+    sizes = info['LVM2_LV_SIZE']
+    if not sizes:
+        sizes = []
+    elif not isinstance(sizes, list):
+        sizes = [sizes]
+
+    return [float(s) / 1024 for s in sizes]
+
+
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/text.py anaconda-storage/text.py
--- anaconda/text.py	2009-02-13 23:03:10.000000000 -0600
+++ anaconda-storage/text.py	2009-02-15 15:40:45.000000000 -0600
@@ -629,10 +629,6 @@
         # draw the frame after setting up the fallback
         self.drawFrame()
 
-        anaconda.id.fsset.registerMessageWindow(self.messageWindow)
-        anaconda.id.fsset.registerProgressWindow(self.progressWindow)
-        anaconda.id.fsset.registerWaitWindow(self.waitWindow)
-
 	lastrc = INSTALL_OK
 	(step, instance) = anaconda.dispatch.currentStep()
 	while step:
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/upgrade.py anaconda-storage/upgrade.py
--- anaconda/upgrade.py	2009-02-13 23:03:10.000000000 -0600
+++ anaconda-storage/upgrade.py	2009-02-17 13:29:52.000000000 -0600
@@ -26,14 +26,13 @@
 import time
 import sys
 import os.path
-import partedUtils
 import string
 import selinux
-import lvm
 from flags import flags
-from fsset import *
 from constants import *
 from product import productName
+from storage import findExistingRootDevices, FSTab
+from storage.formats import getFormat
 
 import rhpl
 import rhpl.arch
@@ -135,10 +134,7 @@
 
     anaconda.id.upgradeRoot = []
     for (dev, fs, meta, label, uuid) in anaconda.id.rootParts:
-        if (root_device is not None) and ((dev == root_device) or (("UUID=%s" % uuid) == root_device) or (("LABEL=%s" % label) == root_device)):
-            anaconda.id.upgradeRoot.append( (dev, fs) )
-        else:
-            anaconda.id.upgradeRoot.append( (dev, fs) )
+        anaconda.id.upgradeRoot.append( (dev, fs) )
 
     if anaconda.id.rootParts is not None and len(anaconda.id.rootParts) > 0:
         anaconda.dispatch.skipStep("findinstall", skip = 0)
@@ -149,7 +145,9 @@
         anaconda.dispatch.skipStep("installtype", skip = 0)
 
 def findExistingRoots(anaconda, upgradeany = 0):
+    # FIXME: this should not live here
     # make ibft configured iscsi disks available
+    # FIXME: this should be handled by the device tree
     anaconda.id.iscsi.startup(anaconda.intf)
 
     if not flags.setupFilesystems:
@@ -157,105 +155,18 @@
         if ((flags.cmdline.has_key("upgradeany")) or
             (upgradeany == 1) or
             (partedUtils.productMatches(relstr, productName))):
-            return [(anaconda.rootPath, 'ext2', "")]
+            return [(anaconda.rootPath, "")]
         return []
 
-    anaconda.id.diskset.openDevices()
-    anaconda.id.partitions.getEncryptedDevices(anaconda.id.diskset)
-    rootparts = anaconda.id.diskset.findExistingRootPartitions(upgradeany = upgradeany)
-
-    # close the devices to make sure we don't leave things sitting open 
-    anaconda.id.diskset.closeDevices()
-
-    # this is a hack... need to clear the skipped disk list after this
-    partedUtils.DiskSet.skippedDisks = []
-    partedUtils.DiskSet.exclusiveDisks = []
-
+    # TODO: figure out why there was a need to clear skippedDisks after this
+    rootparts = findExistingRootDevices(anaconda, upgradeany=upgradeany)
     return rootparts
 
-def getDirtyDevString(dirtyDevs):
-    ret = ""
-    for dev in dirtyDevs:
-        if dev != "loop":
-            ret = "/dev/%s\n" % (dev,)
-        else:
-            ret = "%s\n" % (dev,)
-    return ret
-
-def mountRootPartition(anaconda, rootInfo, oldfsset, allowDirty = 0,
-		       warnDirty = 0, readOnly = 0):
-    (root, rootFs) = rootInfo
-    bindMount = 0
-
-    encryptedDevices = anaconda.id.partitions.encryptedDevices
-    diskset = partedUtils.DiskSet(anaconda)
-    diskset.openDevices()
-    for cryptoDev in encryptedDevices.values():
-        cryptoDev.openDevice()
-    diskset.startMPath()
-    diskset.startDmRaid()
-    diskset.startMdRaid()
-    for cryptoDev in encryptedDevices.values():
-        cryptoDev.openDevice()
-    lvm.vgscan()
-    lvm.vgactivate()
-    for cryptoDev in encryptedDevices.values():
-        cryptoDev.openDevice()
-
-    if root in anaconda.id.partitions.protectedPartitions() and os.path.ismount("/mnt/isodir"):
-        root = "/mnt/isodir"
-        bindMount = 1
-
-    log.info("going to mount %s on %s as %s" %(root, anaconda.rootPath, rootFs))
-    isys.mount(root, anaconda.rootPath, rootFs, bindMount=bindMount)
-
-    oldfsset.reset()
-    newfsset = readFstab(anaconda)
-
-    for entry in newfsset.entries:
-        oldfsset.add(entry)
-
-    if not bindMount:
-        isys.umount(anaconda.rootPath)
-
-    dirtyDevs = oldfsset.hasDirtyFilesystems(anaconda.rootPath)
-    if not allowDirty and dirtyDevs != []:
-        lvm.vgdeactivate()
-        diskset.stopMdRaid()
-        diskset.stopDmRaid()
-        diskset.stopMPath()
-        anaconda.intf.messageWindow(_("Dirty File Systems"),
-                           _("The following file systems for your Linux system "
-                             "were not unmounted cleanly.  Please boot your "
-                             "Linux installation, let the file systems be "
-                             "checked and shut down cleanly to upgrade.\n"
-                             "%s" %(getDirtyDevString(dirtyDevs),)))
-        sys.exit(0)
-    elif warnDirty and dirtyDevs != []:
-        rc = anaconda.intf.messageWindow(_("Dirty File Systems"),
-                                _("The following file systems for your Linux "
-                                  "system were not unmounted cleanly.  Would "
-                                  "you like to mount them anyway?\n"
-                                  "%s" % (getDirtyDevString(dirtyDevs,))),
-                                type = "yesno")
-        if rc == 0:
-            return -1
-
-    if flags.setupFilesystems:
-        for dev, crypto in encryptedDevices.items():
-            if crypto.openDevice():
-                log.error("failed to open encrypted device %s" % (dev,))
-
-        oldfsset.mountFilesystems(anaconda, readOnly = readOnly,
-                                  skiprootfs = bindMount)
-
-    rootEntry = oldfsset.getEntryByMountPoint("/")
-    if (not rootEntry or not rootEntry.fsystem or not rootEntry.fsystem.isMountable()):
-        raise RuntimeError, "/etc/fstab did not list a fstype for the root partition which we support"
-
 def bindMountDevDirectory(instPath):
-    fs = fileSystemTypeGet("bind")
-    fs.mount("/dev", "/dev", bindMount=1, instroot=instPath)
+    getFormat("bind",
+              device="/dev",
+              mountpoint="/dev",
+              exists=True).mount(chroot=instPath)
 
 # returns None if no filesystem exist to migrate
 def upgradeMigrateFind(anaconda):
@@ -297,13 +208,15 @@
 
     fsList = []
 
-    for entry in anaconda.id.fsset.entries:
-        if entry.fsystem.getName() in getUsableLinuxFs():
-            if flags.setupFilesystems and not entry.isMounted():
+    for device in anaconda.id.fsset.devices:
+        if not device.format:
+            continue
+        if device.format.mountable and device.format.linuxNative:
+            if flags.setupFilesystems and not device.format.status:
                 continue
-            space = isys.pathSpaceAvailable(anaconda.rootPath + entry.mountpoint)
+            space = isys.pathSpaceAvailable(anaconda.rootPath + device.format.mountpoint)
             if space > 16:
-                info = (entry.mountpoint, entry.device.getDevice(), space)
+                info = (device, space)
                 fsList.append(info)
 
     suggestion = mem * 2 - swap
@@ -313,57 +226,11 @@
         suggestion = 32
     suggSize = 0
     suggMnt = None
-    for (mnt, part, size) in fsList:
+    for (device, size) in fsList:
 	if (size > suggSize) and (size > (suggestion + 100)):
-	    suggMnt = mnt
+	    suggDev = device
 
-    anaconda.id.upgradeSwapInfo = (fsList, suggestion, suggMnt)
-
-def swapfileExists(swapname):
-    try:
-        os.lstat(swapname)
-	return 1
-    except:
-	return 0
-
-def createSwapFile(instPath, theFsset, mntPoint, size):
-    fstabPath = instPath + "/etc/fstab"
-    prefix = ""
-
-    if mntPoint != "/":
-        file = mntPoint + "/SWAP"
-    else:
-        file = "/SWAP"
-
-    swapFileDict = {}
-    for entry in theFsset.entries:
-        if entry.fsystem.getName() == "swap":
-            swapFileDict[entry.device.getName()] = 1
-        
-    count = 0
-    while (swapfileExists(instPath + file) or 
-	   swapFileDict.has_key(file)):
-	count = count + 1
-	tmpFile = "/SWAP-%d" % (count)
-        if mntPoint != "/":
-            file = mntPoint + tmpFile
-        else:
-            file = tmpFile
-
-    device = SwapFileDevice(file)
-    device.setSize(size)
-    fsystem = fileSystemTypeGet("swap")
-    entry = FileSystemSetEntry(device, "swap", fsystem)
-    entry.setFormat(1)
-    theFsset.add(entry)
-    theFsset.formatEntry(entry, instPath)
-    theFsset.turnOnSwap(instPath, upgrading=True)
-
-    # XXX generalize fstab modification
-    f = open(fstabPath, "a")
-    format = "%-23s %-23s %-7s %-15s %d %d\n";
-    f.write(format % (prefix + file, "swap", "swap", "defaults", 0, 0))
-    f.close()
+    anaconda.id.upgradeSwapInfo = (fsList, suggestion, suggDev)
 
 # XXX handle going backwards
 def upgradeMountFilesystems(anaconda):
@@ -371,21 +238,15 @@
 
     if flags.setupFilesystems:
 	try:
-	    mountRootPartition(anaconda, anaconda.id.upgradeRoot[0], anaconda.id.fsset,
-                               allowDirty = 0)
-        except SystemError:
+	    mountExistingSystem(anaconda,
+                                anaconda.id.upgradeRoot[0],
+                                allowDirty = 0)
+        except Exception:
 	    anaconda.intf.messageWindow(_("Mount failed"),
 		_("One or more of the file systems listed in the "
 		  "/etc/fstab on your Linux system cannot be mounted. "
 		  "Please fix this problem and try to upgrade again."))
 	    sys.exit(0)
-        except RuntimeError:
-            anaconda.intf.messageWindow(_("Mount failed"),
-		_("One or more of the file systems listed in the "
-                  "/etc/fstab of your Linux system are inconsistent and "
-                  "cannot be mounted.  Please fix this problem and try to "
-                  "upgrade again."))
-            sys.exit(0)
 
 	checkLinks = ( '/etc', '/var', '/var/lib', '/var/lib/rpm',
 		       '/boot', '/tmp', '/var/tmp', '/root',
@@ -432,13 +293,11 @@
                                         % (anaconda.rootPath + "/etc/fstab",),
                                         type="ok")
             return DISPATCH_BACK
-            
-	newfsset = readFstab(anaconda)
-        for entry in newfsset.entries:
-            anaconda.id.fsset.add(entry)
+
+        newfsset = FSSet(anaconda.id.devicetree)
+        newfsset.parseFSTab(chroot=anaconda.rootPath)
+        anaconda.id.fsset = newfsset
     if flags.setupFilesystems:
-        if iutil.isPPC():
-            anaconda.id.fsset.formatSwap(anaconda.rootPath, forceFormat=True)
         anaconda.id.fsset.turnOnSwap(anaconda.rootPath, upgrading=True)
         anaconda.id.fsset.mkDevRoot(anaconda.rootPath)
 
diff -urN --exclude .git --exclude po/ --exclude '*.pyc' --exclude '.*.swp' anaconda/yuminstall.py anaconda-storage/yuminstall.py
--- anaconda/yuminstall.py	2009-02-13 23:03:10.000000000 -0600
+++ anaconda-storage/yuminstall.py	2009-02-17 13:31:19.000000000 -0600
@@ -1293,26 +1293,17 @@
         elif iutil.isIA64():
             self.selectPackage("elilo")
 
-    def selectFSPackages(self, fsset, diskset):
-        for entry in fsset.entries:
-            map(self.selectPackage, entry.fsystem.getNeededPackages())
-            if entry.device.crypto:
-                self.selectPackage("cryptsetup-luks")
+    def selectFSPackages(self, storage):
+        for device in storage.fsset.devices:
+            # this takes care of device and filesystem packages
+            map(self.selectPackage, device.packages)
 
-        for disk in diskset.disks.keys():
-            if isys.driveIsIscsi(disk):
+        for disk in storage.disks:
+            if isys.driveIsIscsi(disk.path):
                 log.info("ensuring iscsi is installed")
                 self.selectPackage("iscsi-initiator-utils")
                 break
 
-        if diskset.__class__.mpList:
-            log.info("ensuring device-mapper-multipath is installed")
-            self.selectPackage("device-mapper-multipath")
-        if diskset.__class__.dmList:
-            log.info("ensuring dmraid is installed")
-            self.selectPackage("dmraid")
-
-
     # anaconda requires several programs on the installed system to complete
     # installation, but we have no guarantees that some of these will be
     # installed (they could have been removed in kickstart).  So we'll force
@@ -1335,7 +1326,7 @@
             # New installs only - upgrades will already have all this stuff.
             self.selectBestKernel(anaconda)
             self.selectBootloader()
-            self.selectFSPackages(anaconda.id.fsset, anaconda.id.diskset)
+            self.selectFSPackages(anaconda.id.storage)
             self.selectAnacondaNeeds()
 
         if anaconda.id.getUpgrade():
