]> git.ipfire.org Git - ipfire-3.x.git/commitdiff
Fixed LVM and RAID stuff.
authorMichael Tremer <michael.tremer@ipfire.org>
Wed, 18 Feb 2009 22:30:19 +0000 (23:30 +0100)
committerMichael Tremer <michael.tremer@ipfire.org>
Wed, 18 Feb 2009 22:30:19 +0000 (23:30 +0100)
18 files changed:
src/install/etc/sysconfig/modules [deleted file]
src/pomona/Makefile
src/pomona/autopart.py
src/pomona/backend.py
src/pomona/bootloader.py
src/pomona/cryptodev.py [new file with mode: 0644]
src/pomona/dmraid.py [new file with mode: 0644]
src/pomona/errors.py [new file with mode: 0644]
src/pomona/flags.py
src/pomona/fsset.py
src/pomona/isys/sundries.h [new file with mode: 0644]
src/pomona/lvm.py
src/pomona/packages.py
src/pomona/partRequests.py
src/pomona/partedUtils.py
src/pomona/partitions.py
src/pomona/pomona
src/pomona/tui_partition.py

diff --git a/src/install/etc/sysconfig/modules b/src/install/etc/sysconfig/modules
deleted file mode 100644 (file)
index 479d4a5..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-########################################################################
-# Begin /etc/sysconfig/modules
-#
-# Description : Module auto-loading configuration
-#
-# Authors     :
-#
-# Version     : 00.00
-#
-# Notes       : The syntax of this file is as follows:
-#               <module> [<arg1> <arg2> ...]
-#
-# Each module should be on it's own line, and any options that you want
-# passed to the module should follow it.  The line deliminator is either
-# a space or a tab.
-########################################################################
-
-ext3
-reiserfs
-xfs
-
-# End /etc/sysconfig/modules
index 4b549e1a202d71ba74635783e37f380c5df0fa27..ab6f4978b1c1f744432de4f1350fa4a085c91794 100644 (file)
@@ -37,7 +37,6 @@ install: all
                [ $$? = 0 ] || exit 1; \
        done
        cp -vf *.py lang-{table,names} $(PYTHONLIBDIR)
-       chmod 755 $(PYTHONLIBDIR)/installer.py
        sed -e "s/VERSION/$(VERSION)/g" \
                -e "s/SNAME/$(SNAME)/g" \
                -e "s/PNAME/$(PNAME)/g" \
index 67964f9106641541968c25a0ced064fc8afbee3b..0dcb89ea4d3e15952935ae1324d3bfcbf7ad8ddb 100644 (file)
@@ -304,13 +304,6 @@ def fitSized(diskset, requests, primOnly = 0, newParts = None):
         if requests.isBootable(request):
             drives = getDriveList(request, diskset)
             numDrives = 0 # allocate bootable requests first
-            # FIXME: this is a hack to make sure prep boot is even more first
-            if request.fstype == fsset.fileSystemTypeGet("PPC PReP Boot"):
-                numDrives = -1
-            if request.fstype == fsset.fileSystemTypeGet("Apple Bootstrap"):
-                numDrives = -1
-            if request.fstype == fsset.fileSystemTypeGet("efi"):
-                numDrives = -1
         else:
             drives = getDriveList(request, diskset)
             numDrives = len(drives)
@@ -1169,13 +1162,9 @@ def doClearPartAction(pomona, partitions, diskset):
             continue
 
 def doAutoPartition(pomona):
-    instClass = pomona.id.instClass
     diskset = pomona.id.diskset
     partitions = pomona.id.partitions
 
-    if pomona.isKickstart:
-        partitions.setProtected(pomona.dispatch)
-
     if pomona.dir == DISPATCH_BACK:
         diskset.refreshDevices()
         partitions.setFromDisk(diskset)
@@ -1189,7 +1178,7 @@ def doAutoPartition(pomona):
         # XXX if we noop, then we fail later steps... let's just make it
         # the workstation default.  should instead just never get here
         # if no autopart info
-        instClass.setDefaultPartitioning(partitions, doClear = 0)
+        pomona.setDefaultPartitioning(partitions, doClear = 0)
 
     # reset drive and request info to original state
     # XXX only do this if we're dirty
@@ -1467,30 +1456,19 @@ def doAutoPartition(pomona):
     try:
         doPartitioning(diskset, partitions, doRefresh = 0)
     except PartitioningWarning, msg:
-        if not pomona.isKickstart:
-            pomona.intf.messageWindow(_("Warnings During Automatic Partitioning"),
+        pomona.intf.messageWindow(_("Warnings During Automatic Partitioning"),
                            _("Following warnings occurred during automatic "
                            "partitioning:\n\n%s") % (msg,),
                                custom_icon='warning')
-        else:
-            lvmLog.warning(str(msg))
     except PartitioningError, msg:
         # restore drives to original state
         diskset.refreshDevices()
         partitions.setFromDisk(diskset)
         partitions.setProtected(pomona.dispatch)
-        if not pomona.isKickstart:
-            extra = ""
-            pomona.dispatch.skipStep("partition", skip = 0)
-        else:
-            extra = _("\n\nPress 'OK' to exit the installer.")
+        pomona.dispatch.skipStep("partition", skip = 0)
         pomona.intf.messageWindow(_("Error Partitioning"),
                _("Could not allocate requested partitions: \n\n"
-                 "%s.%s") % (msg, extra), custom_icon='error')
-
-
-        if pomona.isKickstart:
-            sys.exit(0)
+                 "%s.") % (msg,), custom_icon='error')
 
     # now do a full check of the requests
     (errors, warnings) = partitions.sanityCheckAllRequests(diskset)
@@ -1499,10 +1477,7 @@ def doAutoPartition(pomona):
             lvmLog.warning(warning)
     if errors:
         errortxt = string.join(errors, '\n')
-        if pomona.isKickstart:
-            extra = _("\n\nPress 'OK' to exit the installer.")
-        else:
-            extra = _("\n\nPress 'OK' to choose a different partitioning option.")
+        extra = _("\n\nPress 'OK' to choose a different partitioning option.")
 
         pomona.intf.messageWindow(_("Automatic Partitioning Errors"),
                            _("The following errors occurred with your "
@@ -1512,13 +1487,6 @@ def doAutoPartition(pomona):
                              "installation. %s")
                            % (errortxt, extra),
                            custom_icon='error')
-        #
-        # XXX if in kickstart we reboot
-        #
-        if pomona.isKickstart:
-            pomona.intf.messageWindow(_("Unrecoverable Error"),
-                               _("Your system will now be rebooted."))
-            sys.exit(0)
         return DISPATCH_BACK
 
 def autoCreatePartitionRequests(autoreq):
index 6c29ae65e2159470775bf751e9efc107a6e40bd1..325fbe88b6344a8b740df8ebb1bc2d6aca305475 100644 (file)
@@ -36,6 +36,9 @@ class PomonaBackend:
         self.instPath = instPath
         self.modeText = ""
 
+        # some backends may have a special case for rootfs formatting
+        self.skipFormatRoot = False
+
     def doPreSelection(self, intf, id, instPath):
         pass
 
index 90fffd6a903b26c059785fcbfdee88e2f265fac3..755b3eb8a87674c33496ddc3271a8acff791c1af 100644 (file)
@@ -64,24 +64,10 @@ def getBootDevString(line):
 # there's no guarantee that data is written to the disk and grub
 # reads both the filesystem and the disk.  suck.
 def syncDataToDisk(dev, mntpt, instRoot = "/"):
-    import isys, fsset
     isys.sync()
     isys.sync()
     isys.sync()
 
-    # and xfs is even more "special" (#117968)
-    if fsset.isValidXFS(dev):
-        pyfire.executil.execWithRedirect("/usr/sbin/xfs_freeze",
-                                         ["/usr/sbin/xfs_freeze", "-f", mntpt],
-                                         stdout = "/dev/tty5",
-                                         stderr = "/dev/tty5",
-                                         root = instRoot)
-        pyfire.executil.execWithRedirect("/usr/sbin/xfs_freeze",
-                                         ["/usr/sbin/xfs_freeze", "-u", mntpt],
-                                         stdout = "/dev/tty5",
-                                         stderr = "/dev/tty5",
-                                         root = instRoot)
-
 class BootyNoKernelWarning(Exception):
     pass
 
diff --git a/src/pomona/cryptodev.py b/src/pomona/cryptodev.py
new file mode 100644 (file)
index 0000000..0efe94a
--- /dev/null
@@ -0,0 +1,252 @@
+#
+# cryptodev.py
+#
+# Copyright (C) 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): Dave Lehman <dlehman@redhat.com>
+#
+
+import os
+import iutil
+
+import logging
+log = logging.getLogger("pomona")
+
+def isLuks(device):
+    if not device.startswith("/"):
+        device = "/dev/" + device
+    rc = iutil.execWithRedirect("cryptsetup",
+                                ["isLuks", device],
+                                stdout = "/dev/null",
+                                stderr = "/dev/null",
+                                searchPath = 1)
+    if rc:
+        return False
+    else:
+        return True
+
+def luksUUID(device):
+    if not device.startswith("/"):
+        device = "/dev/" + device
+
+    if not isLuks(device):
+        return None
+
+    uuid = iutil.execWithCapture("cryptsetup", ["luksUUID", device])
+    uuid = uuid.strip()
+    return uuid
+
+class LUKSDevice:
+    """LUKSDevice represents an encrypted block device using LUKS/dm-crypt.
+       It requires an underlying block device and a passphrase to become
+       functional."""
+    def __init__(self, device=None, passphrase=None, format=0):
+        self._device = None
+        self.passphrase = ""
+        self.name = ""
+        self.uuid = None
+        self.nameLocked = False
+        self.format = format
+        self.preexist = not format
+        self.packages = ["cryptsetup-luks"]
+        self.scheme = "LUKS"
+
+        self.setDevice(device)
+        self.setPassphrase(passphrase)
+        if self.getUUID():
+            name = "%s-%s" % (self.scheme.lower(), self.uuid)
+            self.setName(name, lock=True)
+
+    def getScheme(self):
+        """Returns the name of the encryption scheme used by the device."""
+        return self.scheme
+
+    def setDevice(self, device):
+        if self._device == device:
+            return
+
+        self._device = device
+        if device is not None:
+            # this will be temporary, but may be useful for debugging
+            name = "%s-%s" % (self.scheme.lower(),
+                              os.path.basename(device))
+            self.setName(name)
+
+    def getDevice(self, encrypted=0):
+        if encrypted:
+            dev = self._device
+        else:
+            dev = "mapper/%s" % (self.name,)
+
+        return dev
+
+    def getUUID(self):
+        if self.format:
+            # self.format means we're going to reformat but haven't yet
+            # so we shouldn't act like there's anything worth seeing there
+            return
+
+        if not self.uuid:
+            self.uuid = luksUUID(self.getDevice(encrypted=1))
+
+        return self.uuid
+
+    def setName(self, name, lock=False):
+        """Set the name of the mapped device, eg: 'dmcrypt-sda3'"""
+        if self.name == name:
+            return
+
+        if self.name and not self.getStatus():
+            raise RuntimeError, "Cannot rename an active mapping."
+
+        if self.nameLocked:
+            log.debug("Failed to change locked mapping name: %s" %
+                      (self.name,))
+            return
+
+        self.name = name
+        if lock and name:
+            # don't allow anyone to lock the name as "" or None
+            self.nameLocked = True
+
+    def setPassphrase(self, passphrase):
+        """Set the (plaintext) passphrase used to access the device."""
+        self.passphrase = passphrase
+
+    def crypttab(self):
+        """Return a crypttab formatted line describing this mapping."""
+        format = "%-23s %-15s %s\n"
+        line = format % (self.name,
+                         "UUID=%s" % (self.getUUID(),),
+                         "none")
+        return line
+
+    def getStatus(self):
+        """0 means active, 1 means inactive (or non-existent)"""
+        if not self.name:
+            return 1
+
+        rc = iutil.execWithRedirect("cryptsetup",
+                                    ["status", self.name],
+                                    stdout = "/dev/null",
+                                    stderr = "/dev/null",
+                                    searchPath = 1)
+        return rc
+
+    def formatDevice(self):
+        """Write a LUKS header onto the device."""
+        if not self.format:
+            return
+
+        if not self.getStatus():
+            log.debug("refusing to format active mapping %s" % (self.name,))
+            return 1
+
+        if not self.passphrase:
+            raise RuntimeError, "Cannot create mapping without a passphrase."
+
+        device = self.getDevice(encrypted=1)
+        if not device:
+            raise ValueError, "Cannot open mapping without a device."
+
+        log.info("formatting %s as %s" % (device, self.getScheme()))
+        p = os.pipe()
+        os.write(p[1], "%s\n" % (self.passphrase,))
+        os.close(p[1])
+
+        rc = iutil.execWithRedirect("cryptsetup",
+                                    ["-q", "luksFormat",
+                                     "/dev/%s" % (device,)],
+                                    stdin = p[0],
+                                    stdout = "/dev/null",
+                                    stderr = "/dev/tty5",
+                                    searchPath = 1)
+        self.format = 0
+        return rc
+
+    def openDevice(self):
+        if not self.getStatus():
+            # already mapped
+            return 0
+
+        if not self.passphrase:
+            raise RuntimeError, "Cannot create mapping without a passphrase."
+
+        device = self.getDevice(encrypted=1)
+        if not device:
+            raise ValueError, "Cannot open mapping without a device."
+
+        uuid = self.getUUID()
+        if not uuid:
+            raise RuntimeError, "Device has no UUID."
+
+        self.setName("%s-%s" % (self.scheme.lower(), uuid), lock=True)
+
+        log.info("mapping %s device %s to %s" % (self.getScheme(),
+                                                 device,
+                                                 self.name))
+
+        p = os.pipe()
+        os.write(p[1], "%s\n" % (self.passphrase,))
+        os.close(p[1])
+
+        rc = iutil.execWithRedirect("cryptsetup",
+                                    ["luksOpen",
+                                     "/dev/%s" % (device,),
+                                     self.name],
+                                    stdin = p[0],
+                                    stdout = "/dev/null",
+                                    stderr = "/dev/tty5",
+                                    searchPath = 1)
+        return rc
+
+    def closeDevice(self):
+        if self.getStatus():
+            # not mapped
+            return 0
+
+        log.info("unmapping %s device %s" % (self.getScheme(), self.name))
+        rc = iutil.execWithRedirect("cryptsetup",
+                                    ["luksClose", self.name],
+                                    stdout = "/dev/null",
+                                    stderr = "/dev/tty5",
+                                    searchPath = 1)
+        return rc
+
+    def addPassphrase(self, newpass):
+        if not newpass:
+            return 1
+
+        if newpass == self.passphrase:
+            return 0
+
+        p = os.pipe()
+        os.write(p[1], "%s\n%s" % (self.passphrase, newpass))
+        os.close(p[1])
+
+        device = self.getDevice(encrypted=1)
+        log.info("adding new passphrase to %s device %s" % (self.getScheme(),
+                                                            device))
+        rc = iutil.execWithRedirect("cryptsetup",
+                                    ["-q",
+                                     "luksAddKey",
+                                     "/dev/%s" % (device,)],
+                                    stdin = p[0],
+                                    stdout = "/dev/null",
+                                    stderr = "/dev/tty5",
+                                    searchPath = 1)
+
+        return rc
diff --git a/src/pomona/dmraid.py b/src/pomona/dmraid.py
new file mode 100644 (file)
index 0000000..5cee469
--- /dev/null
@@ -0,0 +1,302 @@
+#
+# dmraid.py - dmraid probing control
+#
+# Copyright (C) 2005  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): Peter Jones <pjones@redhat.com>
+#
+
+"""DMRaid probing control."""
+# XXX dmraid and md raid should be abstracted from the same thing. -pj
+# XXX dmraid and lvm should use a common control mechanism (such as block.dm)
+#     for device-mapper. -pj
+
+import sys
+import string
+import block
+import partedUtils
+import raid
+from flags import flags
+
+import logging
+from pomona_log import logger, logFile
+
+logger.addLogger ("pomona.dmraid", minLevel=logging.DEBUG)
+log = logging.getLogger("pomona.dmraid")
+logger.addFileHandler (logFile, log)
+
+import isys
+
+# these arches can have their /boot on DMRAID and not have their
+# boot loader blow up
+# XXX This needs to be functional so it can test if drives sit on particular
+# controlers. -pj
+dmraidBootArches = [ "i386", "x86_64" ]
+
+dmNameUpdates = {}
+
+class DmDriveCache:
+    def __init__(self):
+        self.cache = {}
+
+    def _addMapDevs(self, name, devs, obj):
+        isys.cachedDrives["mapper/" + name] = obj
+        log.debug("adding %s to isys cache" % ("mapper/" + name,))
+        for dev in devs:
+            disk = dev.split('/')[-1]
+            if isys.cachedDrives.has_key(disk):
+                self.cache.setdefault(obj.name, {})
+                self.cache[obj.name][obj.name] = obj
+                log.debug("adding %s to dm cache" % (disk,))
+                self.cache[obj.name][disk] = isys.cachedDrives[disk]
+                log.debug("removing %s from isys cache" % (disk,))
+                del isys.cachedDrives[disk]
+
+    def add(self, obj):
+        if isinstance(obj, block.MultiPath):
+            return self._addMapDevs(obj.name, obj.bdevs, obj)
+        else:
+            members = []
+            for m in obj.members:
+                if isinstance(m, block.RaidDev):
+                    members.append(m.rd.device.path)
+            return self._addMapDevs(obj.name, members, obj)
+
+    def remove(self, name):
+        objname = "mapper/" + name
+        if  isys.cachedDrives.has_key(objname):
+            obj = isys.cachedDrives[objname]
+            log.debug("removing %s from isys cache" % (objname,))
+            del isys.cachedDrives[objname]
+            if self.cache.has_key(obj.name):
+                del self.cache[obj.name][obj.name]
+                for k,v in self.cache[obj.name].items():
+                    log.debug("adding %s to isys cache" % (name,))
+                    isys.cachedDrives[k] = v
+                log.debug("removing %s from dm cache" % (obj,))
+                del self.cache[obj.name]
+
+    def rename(self, obj, newname):
+        oldname = 'mapper/' + obj.name
+        if isys.cachedDrives.has_key(oldname):
+            dmNameUpdates[obj.name] = newname
+            self.remove(oldname)
+            # XXX why doesn't setting the property work?
+            obj.set_name(newname)
+            self.add(obj)
+
+    def __contains__(self, name):
+        return self.cache.has_key(name)
+
+cacheDrives = DmDriveCache()
+
+class DegradedRaidWarning(Warning):
+    def __init__(self, *args):
+        self.args = args
+    def __str__(self):
+        return self.args and ('%s' % self.args[0]) or repr(self)
+
+def scanForRaid(drives, degradedOk=False):
+    """Scans for dmraid devices on drives.
+
+    drives is a list of device names.
+    Returns a list of (raidSet, parentRaidSet, devices, level, totalDisks)
+      tuples.
+    """
+
+    log.debug("scanning for dmraid on drives %s" % (drives,))
+
+    probeDrives = []
+    for d in drives:
+        probeDrives.append("/dev/%s" % (d,))
+
+    dmsets = []
+    def nonDegraded(rs):
+        log.debug("got raidset %s (%s)" % (rs, string.join(rs.member_devpaths)))
+        log.debug("  valid: %s found_devs: %s total_devs: %s" % (rs.valid, rs.rs.found_devs, rs.rs.total_devs))
+
+        if not rs.valid and not degradedOk:
+            log.warning("raid %s (%s) is degraded" % (rs, rs.name))
+            #raise DegradedRaidWarning, rs
+            return False
+        return True
+
+    raidsets = filter(nonDegraded, block.getRaidSets(probeDrives) or [])
+    def updateName(rs):
+        if dmNameUpdates.has_key(rs.name):
+            rs.set_name(dmNameUpdates[rs.name])
+        cacheDrives.add(rs)
+        return rs
+
+    return reduce(lambda x,y: x + [updateName(y),], raidsets, [])
+
+def renameRaidSet(rs, name):
+    cacheDrives.rename(rs, name)
+
+def startAllRaid(driveList):
+    """Do a raid start on raid devices."""
+
+    if not flags.dmraid:
+        return []
+    log.debug("starting all dmraids on drives %s" % (driveList,))
+
+    try:
+        dmList = scanForRaid(driveList)
+    except Exception, e:
+        log.error("error scanning dmraid, disabling: %s" %(e,))
+        flags.dmraid = 0
+        dmList = []
+
+    newDmList = []
+    for rs in dmList:
+        rs.prefix = '/dev/mapper/'
+        log.debug("starting raid %s with mknod=True" % (rs,))
+        try:
+            rs.activate(mknod=True)
+            newDmList.append(rs)
+        except Exception, e:
+            log.error("Activating raid %s failed: " % (rs.rs,))
+            log.error("  table: %s" % (rs.rs.table,))
+            log.error("  exception: %s" % (e,))
+            try:
+                rs.deactivate()
+                del rs
+            except:
+                pass
+
+    return newDmList
+
+def stopAllRaid(dmList):
+    """Do a raid stop on each of the raid device tuples given."""
+
+    if not flags.dmraid:
+        return
+    log.debug("stopping all dmraids")
+    for rs in dmList:
+        log.debug("stopping raid %s" % (rs,))
+        if rs.name in cacheDrives:
+            cacheDrives.remove(rs.name)
+
+            rs.deactivate()
+            #block.removeDeviceMap(map)
+
+def isRaid6(raidlevel):
+    """Return whether raidlevel is a valid descriptor of RAID6."""
+    return False
+
+def isRaid5(raidlevel):
+    """Return whether raidlevel is a valid descriptor of RAID5."""
+    return False
+
+def isRaid1(raidlevel):
+    """Return whether raidlevel is a valid descriptor of RAID1."""
+    return raid.isRaid1(raidlevel)
+
+def isRaid0(raidlevel):
+    """Return whether raidlevel is a valid descriptor of RAID1."""
+    return raid.isRaid0(raidlevel)
+
+def get_raid_min_members(raidlevel):
+    """Return the minimum number of raid members required for raid level"""
+    return raid.get_raid_min_members(raidlevel)
+
+def get_raid_max_spares(raidlevel, nummembers):
+    """Return the maximum number of raid spares for raidlevel."""
+    return raid.get_raid_max_spares(raidlevel, nummembers)
+
+def register_raid_device(dmname, newdevices, newlevel, newnumActive):
+    """Register a new RAID device in the dmlist."""
+    raise NotImplementedError
+
+def lookup_raid_device(dmname):
+    """Return the requested RAID device information."""
+    for rs, parent, devices, level, nrDisks, totalDisks in \
+            partedUtils.DiskSet.dmList:
+        if dmname == rs.name:
+            return (rs.name, devices, level, totalDisks)
+    raise KeyError, "dm device not found"
+
+def scanForMPath(drives):
+    log.debug("scanning for multipath on drives %s" % (drives,))
+    mpaths = []
+
+    probeDrives = []
+    for d in drives:
+        probeDrives.append("/dev/%s" % (d,))
+
+    import block as _block
+
+    log.debug("loading bdevid modules from: '%s'" % (_block.getBdevidPath(),))
+
+    _block.load("scsi")
+    mpaths = _block.getMPaths(probeDrives)
+    log.debug("mpaths: %s" % (mpaths,))
+
+    def updateName(mp):
+        if dmNameUpdates.has_key(mp.name):
+            mp.set_name(dmNameUpdates[mp.name])
+        cacheDrives.add(mp)
+        return mp
+
+    return reduce(lambda x,y: x + [updateName(y),], mpaths, [])
+
+def renameMPath(mpath, name):
+    cacheDrives.rename(mpath, name)
+
+def startMPath(mpath):
+    if flags.mpath == 0:
+        return
+    mpath.prefix = '/dev/mapper/'
+    log.debug("starting mpath %s with mknod=True" % (mpath,))
+    mpath.activate(mknod=True)
+
+def startAllMPath(driveList):
+    """Start all of the MPaths of the specified drives."""
+
+    if not flags.mpath:
+        return []
+    log.debug("starting all mpaths on drives %s" % (driveList,))
+
+    try:
+        mpList = scanForMPath(driveList)
+    except Exception, e:
+        log.error("error scanning mpaths, disabling: %s" %(e,))
+        flags.mpath = 0
+        mpList = []
+
+    for mp in mpList:
+        startMPath(mp)
+    return mpList
+
+def stopMPath(mp):
+    if flags.mpath == 0:
+        return
+
+    log.debug("stopping mpath %s" % (mp,))
+    if mp.name in cacheDrives:
+        cacheDrives.remove(mp.name)
+
+        mp.deactivate()
+        #block.removeDeviceMap(map)
+
+def stopAllMPath(mpList):
+    """Do a mpath stop on each of the mpath device tuples given."""
+
+    if not flags.mpath:
+        return
+    log.debug("stopping all mpaths")
+    for mp in mpList:
+        stopMPath(mp)
diff --git a/src/pomona/errors.py b/src/pomona/errors.py
new file mode 100644 (file)
index 0000000..6708b1d
--- /dev/null
@@ -0,0 +1,159 @@
+#
+# errors.py: exception classes used throughout pomona
+#
+# Copyright (C) 2002, 2007, 2008  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): Peter Jones <pjones@redhat.com>
+#            Chris Lumens <clumens@redhat.com>
+#            Matt Wilson <msw@redhat.com>
+#            Jeremy Katz <katzj@redhat.com>
+#            Mike Fulbright <msf@redhat.com>
+#
+
+import string
+import os
+from constants import lvmErrorOutput
+
+"""Exceptions for use in lvm operations."""
+
+class LvmError(Exception):
+    """An error occurred with lvm."""
+    def __init__(self, command, name=None):
+        self.command = command
+        self.name = name
+        self.log = self.getLvmOutput()
+
+    def getLvmOutput(self):
+        if not os.access(lvmErrorOutput, os.R_OK):
+            return ""
+        f = open(lvmErrorOutput, "r")
+        lines = reduce(lambda x,y: x + [string.strip(y),], f.readlines(), [])
+        lines = string.join(reduce(lambda x,y: x + ["   %s" % (y,)], \
+                                    lines, []), "\n")
+        return lines
+
+    def __str__(self):
+        s = ""
+        if not self.name is None:
+            s = " for device %s" % (self.name,)
+        return "%s failed%s\nLog:\n%s" % (self.command, s, self.log)
+
+class LVCreateError(LvmError):
+    def __init__(self, vgname, lvname, size):
+        self.vgname = vgname
+        self.lvname = lvname
+        self.size = size
+        self.log = self.getLvmOutput()
+
+    def __str__(self):
+        return "lvcreate of %d Megabyte lv \"%s\" on vg \"%s\" failed\n" \
+               "Log:\n%s" % ( \
+            self.size, self.lvname, self.vgname, self.log)
+
+class LVRemoveError(LvmError):
+    def __init__(self, vgname, lvname):
+        self.vgname = vgname
+        self.lvname = lvname
+        self.log = self.getLvmOutput()
+
+    def __str__(self):
+        return "lvremove of lv \"%s\" from vg \"%s\" failed\nLog:\n%s" % ( \
+            self.lvname, self.vgname, self.log)
+
+class LVResizeError(LvmError):
+    def __init__(self, vgname, lvname):
+        self.vgname = vgname
+        self.lvname = lvname
+        self.log = self.getLvmOutput()
+
+    def __str__(self):
+        return "lvresize of lv \"%s\" from vg \"%s\" failed\nLog:\n%s" % ( \
+            self.lvname, self.vgname, self.log)
+
+class VGCreateError(LvmError):
+    def __init__(self, vgname, PESize, nodes):
+        self.vgname = vgname
+        self.PESize = PESize
+        self.nodes = nodes
+        self.log = self.getLvmOutput()
+
+    def __str__(self):
+        nodes = string.join(self.nodes, ' ')
+        return "vgcreate failed creating vg \"%s\" (PESize=%dkB) on PVs: %s\n" \
+               "Log:\n%s" % ( \
+            self.vgname, self.PESize, nodes, self.log)
+
+class VGRemoveError(LvmError):
+    def __init__(self, vgname):
+        self.vgname = vgname
+        self.log = self.getLvmOutput()
+
+    def __str__(self):
+        return "vgremove of vg \"%s\" failed\nLog:\n%s" % ( \
+            self.vgname, self.log)
+
+class PVRemoveError(LvmError):
+    def __init__(self, pvname):
+        self.pvname = pvname
+        self.log = self.getLvmOutput()
+
+    def __str__(self):
+        return "pvremove of pv \"%s\" failed\nLog:\n%s" % ( \
+            self.pvname, self.log)
+
+class PVCreateError(LvmError):
+    def __init__(self, pvname):
+        self.pvname = pvname
+        self.log = self.getLvmOutput()
+
+    def __str__(self):
+        return "pvcreate of pv \"%s\" failed\nLog:\n%s" % ( \
+            self.pvname, self.log)
+
+"""Exceptions for use in partitioning."""
+
+class PartitioningError(Exception):
+    """A critical error which must be resolved to continue the installation."""
+    def __init__(self, message=""):
+        self.message = str(message)
+
+    def __str__ (self):
+        return self.message
+
+class PartitioningWarning(Exception):
+    """A warning which may be ignored and still complete the installation."""
+    def __init__(self, message=""):
+        self.message = str(message)
+
+    def __str__ (self):
+        return self.message
+
+class LabelError(Exception):
+    """The device could not be labeled."""
+    def __init__(self, message=""):
+        self.message = str(message)
+
+    def __str__(self):
+        return self.message
+
+"""Exceptions for use in package selection."""
+
+class NoSuchGroup(Exception):
+    def __init__ (self, value):
+        self.value = value
+
+    def __str__ (self):
+        return self.value
index 7ff178b914cd2dead5f262a976a39e800f8b018a..69b3768f65c403afc7336104443cfcfcb22c64e4 100644 (file)
@@ -50,6 +50,8 @@ class Flags:
         self.__dict__['flags']['debug'] = 0
         self.__dict__['flags']['cmdline'] = self.createCmdlineDict()
         self.__dict__['flags']['network'] = False
+        self.__dict__['flags']['mpath'] = 1
+        self.__dict__['flags']['dmraid'] = 1
 
         for line in os.popen("tty"):
             line = line.strip()
index 5510eeed2f1717a6251b802ac5f4a3d76bcf55a5..1d0d3c3d82ecb408f664ba3c59ed124f0415d893 100644 (file)
@@ -701,9 +701,9 @@ fileSystemTypeRegister(ext3FileSystem())
 class ext4FileSystem(extFileSystem):
     def __init__(self):
         extFileSystem.__init__(self)
-        self.name = "ext4dev"
+        self.name = "ext4"
         self.partedFileSystemType = parted.file_system_type_get("ext3")
-        self.extraFormatArgs = [ "-t", "ext4dev" ]
+        self.extraFormatArgs = [ "-t", "ext4" ]
         self.bootable = False
 
         # this is way way experimental at present...
diff --git a/src/pomona/isys/sundries.h b/src/pomona/isys/sundries.h
new file mode 100644 (file)
index 0000000..1fb67b8
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * sundries.h: Support function prototypes.  Functions are in sundries.c.
+ *
+ * Copyright (C) 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/>.
+ */
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#if !defined(bool_t) && !defined(__GLIBC__)
+#include <rpc/types.h>
+#endif
+
+extern int mount_quiet;
+extern int verbose;
+extern int sloppy;
+
+#define streq(s, t)    (strcmp ((s), (t)) == 0)
+
+
+/* String list data structure.  */
+typedef struct string_list
+{
+  char *hd;
+  struct string_list *tl;
+} *string_list;
+
+#define car(p) ((p) -> hd)
+#define cdr(p) ((p) -> tl)
+
+string_list cons (char *a, const string_list);
+
+/* Functions in sundries.c that are used in mount.c and umount.c  */
+void block_signals (int how);
+char *canonicalize (const char *path);
+char *realpath (const char *path, char *resolved_path);
+void error (const char *fmt, ...);
+int matching_type (const char *type, string_list types);
+string_list parse_list (char *strings);
+void *xmalloc (size_t size);
+char *xstrdup (const char *s);
+char *xstrndup (const char *s, int n);
+char *xstrconcat2 (const char *, const char *);
+char *xstrconcat3 (const char *, const char *, const char *);
+char *xstrconcat4 (const char *, const char *, const char *, const char *);
+
+/* Here is some serious cruft.  */
+#ifdef __GNUC__
+#if __GNUC__ > 2 || (defined(__GNUC_MINOR__) && __GNUC__ == 2 && __GNUC_MINOR__ >= 5)
+void die (int errcode, const char *fmt, ...) __attribute__ ((noreturn));
+#else /* GNUC < 2.5 */
+void volatile die (int errcode, const char *fmt, ...);
+#endif /* GNUC < 2.5 */
+#else /* !__GNUC__ */
+void die (int errcode, const char *fmt, ...);
+#endif /* !__GNUC__ */
+
+#ifdef HAVE_NFS
+int nfsmount (const char *spec, const char *node, int *flags,
+             char **orig_opts, char **opt_args, int running_bg);
+#endif
+
+/* exit status - bits below are ORed */
+#define EX_USAGE       1       /* incorrect invocation or permission */
+#define EX_SYSERR      2       /* out of memory, cannot fork, ... */
+#define EX_SOFTWARE    4       /* internal mount bug or wrong version */
+#define EX_USER                8       /* user interrupt */
+#define EX_FILEIO      16      /* problems writing, locking, ... mtab/fstab */
+#define EX_FAIL               32       /* mount failure */
+#define EX_SOMEOK      64      /* some mount succeeded */
+
+#define EX_BG         256       /* retry in background (internal only) */
index bd708d33c731f95529428c4ec49deedcec809690..7531964ccf4ca01d9e83454ba95aa06a2833e606 100644 (file)
@@ -63,6 +63,11 @@ def has_lvm():
 # now check to see if lvm is available
 has_lvm()
 
+if lvmDevicePresent == 1:
+       log.info("LVM is enabled.")
+else:
+       log.info("LVM is disabled.")
+
 def lvmExec(*args):
     try:
         return iutil.execWithRedirect("lvm", args, stdout = lvmErrorOutput,
@@ -86,7 +91,7 @@ def vgscan():
     """Runs vgscan."""
     global lvmDevicePresent
 
-    if flags.test or lvmDevicePresent == 0:
+    if lvmDevicePresent == 0:
         return
 
     rc = lvmExec("vgscan", "-v")
@@ -110,7 +115,7 @@ def vgcheckactive(volgroup = None):
     volgroup - optional parameter to inquire about a specific volume group.
     """
     global lvmDevicePresent
-    if flags.test or lvmDevicePresent == 0:
+    if lvmDevicePresent == 0:
         return False
 
     args = ["lvs", "--noheadings", "--units", "b", "--nosuffix",
@@ -136,7 +141,7 @@ def vgactivate(volgroup = None):
     volgroup - optional single volume group to activate
     """
     global lvmDevicePresent
-    if flags.test or lvmDevicePresent == 0:
+    if lvmDevicePresent == 0:
         return
 
     args = ["vgchange", "-ay", "-v"]
@@ -154,7 +159,7 @@ def vgdeactivate(volgroup = None):
     volgroup - optional single volume group to deactivate
     """
     global lvmDevicePresent
-    if flags.test or lvmDevicePresent == 0:
+    if lvmDevicePresent == 0:
         return
 
     args = ["vgchange", "-an", "-v"]
@@ -173,7 +178,7 @@ def lvcreate(lvname, vgname, size):
     size - size of lv, in megabytes.
     """
     global lvmDevicePresent
-    if flags.test or lvmDevicePresent == 0:
+    if lvmDevicePresent == 0:
         return
     writeForceConf()
     vgscan()
@@ -194,7 +199,7 @@ def lvremove(lvname, vgname):
     vgname - name of volume group lv is in.
     """
     global lvmDevicePresent
-    if flags.test or lvmDevicePresent == 0:
+    if lvmDevicePresent == 0:
         return
 
     args = ["lvremove", "-f", "-v"]
@@ -210,7 +215,7 @@ def lvremove(lvname, vgname):
 
 def lvresize(lvname, vgname, size):
     global lvmDevicePresent
-    if flags.test or lvmDevicePresent == 0:
+    if lvmDevicePresent == 0:
         return
 
     args = ["lvresize", "-An", "-L", "%dM" %(size,), "-v", "--force",
@@ -232,7 +237,7 @@ def vgcreate(vgname, PESize, nodes):
     nodes - LVM Physical Volumes on which to put the new VG.
     """
     global lvmDevicePresent
-    if flags.test or lvmDevicePresent == 0:
+    if lvmDevicePresent == 0:
         return
 
     # rescan now that we've recreated pvs.  ugh.
@@ -256,7 +261,7 @@ def vgremove(vgname):
     vgname - name of volume group.
     """
     global lvmDevicePresent
-    if flags.test or lvmDevicePresent == 0:
+    if lvmDevicePresent == 0:
         return
 
     # find the Physical Volumes which make up this Volume Group, so we
@@ -313,7 +318,7 @@ def pvcreate(node):
     node - path to device node on which to create the new PV."
     """
     global lvmDevicePresent
-    if flags.test or lvmDevicePresent == 0:
+    if lvmDevicePresent == 0:
         return
 
     # rescan now that we've recreated pvs.  ugh.
index 3bb39be4fec6c848d15e79a82e561c639aeab206..27dac7eb54b5a7388def5ce07b70d4fb29d2db52 100644 (file)
@@ -22,6 +22,8 @@ import os
 import sys
 import fsset
 import shutil
+import time
+import lvm
 from flags import flags
 from constants import *
 
@@ -45,34 +47,91 @@ def copyPomonaLogs(pomona):
             except:
                 pass
 
+def doMigrateFilesystems(pomona):
+    if pomona.dir == DISPATCH_BACK:
+        return DISPATCH_NOOP
+
+    if pomona.id.fsset.haveMigratedFilesystems():
+        return DISPATCH_NOOP
+
+    pomona.id.fsset.migrateFilesystems (pomona)
+
 def turnOnFilesystems(pomona):
+    def handleResizeError(e, dev):
+        if os.path.exists("/tmp/resize.out"):
+            details = open("/tmp/resize.out", "r").read()
+        else:
+            details = "%s" %(e,)
+        pomona.intf.detailedMessageWindow(_("Resizing Failed"),
+                                            _("There was an error encountered "
+                                            "resizing the device %s.") %(dev,),
+                                            details,
+                                            type = "custom",
+                                            custom_buttons = [_("_Exit installer")])
+        sys.exit(1)
+
     if pomona.dir == DISPATCH_BACK:
         log.info("unmounting filesystems")
         pomona.id.fsset.umountFilesystems(pomona.rootPath)
         return
 
-    #pomona.id.partitions.doMetaDeletes(pomona.id.diskset)
-    pomona.id.diskset.clearDevices()
+    if not pomona.id.fsset.isActive():
+        # turn off any swaps that we didn't turn on
+        # needed for live installs
+        iutil.execWithRedirect("swapoff", ["-a"],
+                               stdout = "/dev/tty5", stderr="/dev/tty5",
+                               searchPath = 1)
+    pomona.id.partitions.doMetaDeletes(pomona.id.diskset)
     pomona.id.fsset.setActive(pomona.id.diskset)
+    try:
+        pomona.id.fsset.shrinkFilesystems(pomona.id.diskset, pomona.rootPath)
+    except fsset.ResizeError, (e, dev):
+        handleResizeError(e, dev)
+
     if not pomona.id.fsset.isActive():
         pomona.id.diskset.savePartitions()
-    pomona.id.fsset.checkBadblocks(pomona.rootPath)
-    pomona.id.fsset.formatSwap(pomona.rootPath)
-    pomona.id.fsset.turnOnSwap(pomona.rootPath)
-    pomona.id.fsset.makeFilesystems(pomona.rootPath)
-    pomona.id.fsset.mountFilesystems(pomona)
+        # this is somewhat lame, but we seem to be racing with
+        # device node creation sometimes.  so wait for device nodes
+        # to settle
+        w = pomona.intf.waitWindow(_("Activating"), _("Activating new partitions.  Please wait..."))
+        time.sleep(1)
+        rc = iutil.execWithRedirect("/sbin/udevadm", [ "settle" ],
+                                    stdout = "/dev/tty5",
+                                    stderr = "/dev/tty5",
+                                    searchPath = 1)
+        w.pop()
+
+        pomona.id.partitions.doEncryptionRetrofits()
 
-def doMigrateFilesystems(pomona):
-    if pomona.dir == DISPATCH_BACK:
-        return DISPATCH_NOOP
+        try:
+            pomona.id.partitions.doMetaResizes(pomona.id.diskset)
+        except lvm.LVResizeError, e:
+            handleResizeError("%s" %(e,), "%s/%s" %(e.vgname, e.lvname))
 
-    if pomona.id.fsset.haveMigratedFilesystems():
-        return DISPATCH_NOOP
+    try:
+        pomona.id.fsset.growFilesystems(pomona.id.diskset, pomona.rootPath)
+    except fsset.ResizeError, (e, dev):
+        handleResizeError(e, dev)
+
+    if not pomona.id.fsset.volumesCreated:
+        try:
+            pomona.id.fsset.createLogicalVolumes(pomona.rootPath)
+        except SystemError, e:
+            log.error("createLogicalVolumes failed with %s", str(e))
+            pomona.intf.messageWindow(_("LVM operation failed"),
+                                str(e)+"\n\n"+_("The installer will now exit..."),
+                                type="custom", custom_icon="error", custom_buttons=[_("_Reboot")])
+            sys.exit(0)
 
-    pomona.id.fsset.migrateFilesystems(pomona)
+    pomona.id.fsset.formatSwap(pomona.rootPath)
+    pomona.id.fsset.turnOnSwap(pomona.rootPath)
+    pomona.id.fsset.makeFilesystems(pomona.rootPath,
+                                      pomona.backend.skipFormatRoot)
+    pomona.id.fsset.mountFilesystems(pomona,0,0,
+                                       pomona.backend.skipFormatRoot)
 
 def setupTimezone(pomona):
-    # we don't need this going backwards
+    # we don't need this on going backwards
     if pomona.dir == DISPATCH_BACK:
         return
 
@@ -89,13 +148,10 @@ def setupTimezone(pomona):
     args = [ "--hctosys" ]
     if pomona.id.timezone.utc:
         args.append("-u")
-    elif pomona.id.timezone.arc:
-        #args.append("-a")
-        args.append("-l")
 
     try:
-        iutil.execWithRedirect("/sbin/hwclock", args, stdin = None,
-                                stdout = "/dev/tty5", stderr = "/dev/tty5")
+        iutil.execWithRedirect("/usr/sbin/hwclock", args, stdin = None,
+                               stdout = "/dev/tty5", stderr = "/dev/tty5")
     except RuntimeError:
         log.error("Failed to set clock")
 
index 73ec628eb8e40273362c8cd02f0a0ae104336f79..25f70b7d9189bc9720c549d07c5d9031da8845e6 100644 (file)
@@ -31,7 +31,6 @@ import string
 import os, sys, math
 
 from constants import *
-from flags import *
 
 import gettext
 _ = lambda x: gettext.ldgettext("pomona", x)
@@ -271,14 +270,6 @@ class RequestSpec:
         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 ["ext3", "ext2"]:
-            return _("The mount point %s must be formatted 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 "
index cee5a1e7bd0ed71cc1f433641b1d6e254ba68d27..8814921a2800f7d8f512d3261924ae077ad79c4b 100644 (file)
@@ -36,7 +36,7 @@ import raid
 import dmraid
 import block
 import lvm
-import traceback
+import inspect
 from flags import flags
 from errors import *
 from constants import *
@@ -79,7 +79,7 @@ def start_sector_to_cyl(device, sector):
                            / (device.heads * device.sectors)) + 1))
 
 def end_sector_to_cyl(device, sector):
-    """Return the closest cylinder (round up) to sector on device."""
+    """Return the closest cylinder (round up) to sector on device."""    
     return int(math.ceil(float((sector + 1))
                          / (device.heads * device.sectors)))
 
@@ -88,7 +88,7 @@ def start_cyl_to_sector(device, cyl):
     return long((cyl - 1) * (device.heads * device.sectors))
 
 def end_cyl_to_sector(device, cyl):
-    "Return the sector corresponding to cylinder as a ending cylinder."
+    "Return the sector corresponding to cylinder as a ending cylinder."    
     return long(((cyl) * (device.heads * device.sectors)) - 1)
 
 def getPartSize(partition):
@@ -124,7 +124,7 @@ def getMaxAvailPartSizeMB(part):
     return math.floor(maxlen * part.geom.dev.sector_size / 1024.0 / 1024.0)
 
 def get_partition_by_name(disks, partname):
-    """Return the parted part object associated with partname.
+    """Return the parted part object associated with partname.  
 
     Arguments:
     disks -- Dictionary of diskname->PedDisk objects
@@ -232,7 +232,7 @@ def get_max_logical_partitions(disk):
     return 11
 
 def map_foreign_to_fsname(type):
-    """Return the partition type associated with the numeric type."""
+    """Return the partition type associated with the numeric type.""" 
     if type in allPartitionTypesDict.keys():
         return allPartitionTypesDict[type]
     else:
@@ -321,8 +321,11 @@ def labelDisk(deviceFile, forceLabelType=None):
 def checkDiskLabel(disk, intf):
     """Check that the disk label on disk is valid for this machine type."""
     arch = iutil.getArch()
-    if not arch in archLabels.keys() and disk.type.name == "msdos":
-        return 0
+    if arch in archLabels.keys():
+        pass
+    else:
+        if disk.type.name == "msdos":
+            return 0
 
     if intf:
         rc = intf.messageWindow(_("Warning"),
@@ -375,7 +378,7 @@ def validateFsType(part):
     # if the partition already has a type, no need to search
     if part.fs_type:
         return
-
+    
     # first fsystem to probe wins, so sort the types into a preferred
     # order.
     fsnames = fsTypes.keys()
@@ -396,7 +399,7 @@ def validateFsType(part):
             # in the case where a user does not modify partitions
             part.set_system(fstype)
             return
-
+            
 def isLinuxNativeByNumtype(numtype):
     """Check if the type is a 'Linux native' filesystem."""
     linuxtypes = [0x82, 0x83, 0x8e, 0xfd]
@@ -408,15 +411,15 @@ def isLinuxNativeByNumtype(numtype):
     return 0
 
 def sniffFilesystemType(device):
-    """Sniff to determine the type of fs on device.
+    """Sniff to determine the type of fs on device.  
 
     device - name of device to sniff.
     """
     return isys.readFSType(device)
 
 def getReleaseString(mountpoint):
-    if os.access(mountpoint + "/etc/system-release", os.R_OK):
-        f = open(mountpoint + "/etc/system-release", "r")
+    if os.access(mountpoint + "/etc/redhat-release", os.R_OK):
+        f = open(mountpoint + "/etc/redhat-release", "r")
         try:
             lines = f.readlines()
         except IOError:
@@ -429,7 +432,7 @@ def getReleaseString(mountpoint):
         # return the first line with the newline at the end stripped
         if len(lines) == 0:
             return ""
-        relstr = string.strip(lines[0][:-1])
+       relstr = string.strip(lines[0][:-1])
 
         # get the release name and version
         # assumes that form is something
@@ -496,7 +499,7 @@ class DiskSet:
 
     def renameMPath(self, mp, name):
         dmraid.renameMPath(mp, name)
-
     def stopMPath(self):
         """Stop all of the mpath devices associated with the DiskSet."""
 
@@ -527,6 +530,7 @@ class DiskSet:
 
     def stopDmRaid(self):
         """Stop all of the dmraid devices associated with the DiskSet."""
+
         if DiskSet.dmList:
             dmraid.stopAllRaid(DiskSet.dmList)
             DiskSet.dmList = None
@@ -598,6 +602,14 @@ class DiskSet:
                 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():
@@ -669,12 +681,11 @@ class DiskSet:
                 except SystemError:
                     pass
 
-            if found:
                 isys.umount(self.pomona.rootPath)
 
         # now, look for candidate lvm roots
-        lvm.vgscan()
-        lvm.vgactivate()
+       lvm.vgscan()
+       lvm.vgactivate()
 
         for dev, crypto in self.pomona.id.partitions.encryptedDevices.items():
             # FIXME: order these so LVM and RAID always work on the first try
@@ -703,10 +714,7 @@ class DiskSet:
                 except SystemError:
                     pass
 
-            if found:
-                isys.umount(self.pomona.rootPath)
-
-        lvm.vgdeactivate()
+       lvm.vgdeactivate()
 
         # don't stop raid until after we've looked for lvm on top of it
         self.stopMdRaid()
@@ -731,6 +739,12 @@ class DiskSet:
                     theDev = node
                     if part.fs_type:
                         fstype = part.fs_type.name
+                    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()
@@ -750,20 +764,18 @@ class DiskSet:
                         part = disk.next_partition(part)
                         continue
 
-                    isys.umount(self.pomona.rootPath)
-
                 part = disk.next_partition(part)
         return rootparts
 
     def driveList (self):
         """Return the list of drives on the system."""
-        drives = isys.hardDriveDict().keys()
-        drives.sort (isys.compareDrives)
-        return drives
+       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()
+       return isys.hardDriveDict()
 
     def savePartitions (self):
         """Write the partition tables out to the disks."""
@@ -783,13 +795,6 @@ class DiskSet:
                 del disk
                 continue
 
-            # FIXME: this belongs in parted itself, but let's do a hack...
-            if iutil.isX86() and disk.type.name == "gpt":
-                log.debug("syncing gpt to mbr for disk %s" % (disk.dev.path,))
-                iutil.execWithRedirect("gptsync", [disk.dev.path,],
-                                       stdout="/tmp/gptsync.log",
-                                       stderr="/tmp/gptsync.err",
-                                       searchPath = 1)
             del disk
         self.refreshDevices()
 
@@ -826,6 +831,26 @@ class DiskSet:
             del self.disks[disk]
         self.devicesOpen = False
 
+    def isDisciplineFBA (self, drive):
+        drive = drive.replace('/dev/', '')
+
+        if drive.startswith("dasd"):
+            discipline = "/sys/block/%s/device/discipline" % (drive,)
+            if os.path.isfile(discipline):
+                try:
+                    fp = open(discipline, "r")
+                    lines = fp.readlines()
+                    fp.close()
+
+                    if len(lines) == 1:
+                        if lines[0].strip() == "FBA":
+                            return True
+                except:
+                    log.error("failed to check discipline of %s" % (drive,))
+                    pass
+
+        return False
+
     def _askForLabelPermission(self, intf, drive, clearDevs, initAll, ks):
         #Do not try to initialize device's part. table in rescue mode
         if self.pomona.rescue:
@@ -833,7 +858,8 @@ class DiskSet:
             return False
 
         rc = 0
-        if (ks and (drive in clearDevs) and initAll):
+        if (ks and (drive in clearDevs) and initAll) or \
+           self.isDisciplineFBA(drive):
             rc = 1
         elif intf:
             deviceFile = "/dev/" + drive
@@ -863,7 +889,6 @@ class DiskSet:
 
         try:
             try:
-                # FIXME: need the right fix for z/VM formatted dasd
                 disk = labelDisk(deviceFile)
             except parted.error, msg:
                 log.error("parted error: %s" % (msg,))
@@ -920,11 +945,6 @@ class DiskSet:
             clearDevs = []
             initAll = False
 
-            if self.pomona.isKickstart:
-                ks = True
-                clearDevs = self.pomona.id.ksdata.clearpart.drives
-                initAll = self.pomona.id.ksdata.clearpart.initAll
-
             if initAll and ((clearDevs is None) or (len(clearDevs) == 0) \
                        or (drive in clearDevs)) and not flags.test \
                        and not hasProtectedPartitions(drive, self.pomona):
@@ -976,7 +996,7 @@ class DiskSet:
                         "of this disk or use any partitions beyond /dev/%s15 "
                         "in %s") % (drive, drive, name)
 
-                rc = intf.messageWindow(_("Warning"), str,
+                rc = intf.messageWindow(_("Warning"), str, 
                                     type="custom",
                                     custom_buttons = [_("_Reboot"),
                                                       _("_Continue")],
@@ -1014,7 +1034,7 @@ class DiskSet:
                         ptype = None
                     rc.append((device, ptype))
                 part = disk.next_partition (part)
-
+      
         return rc
 
     def diskState (self):
@@ -1060,7 +1080,7 @@ class DiskSet:
                                  "of this problem."))
             return True
         return False
-
+    
 
     def exceptionDisks(self, pomona, probe=True):
         if probe:
@@ -1151,7 +1171,7 @@ allPartitionTypesDict = {
     0xe1: "DOS access",
     0xe3: "DOS R/O",
     0xeb: "BEOS",
-    0xee: "EFI GPT",
+    0xee: "EFI GPT",    
     0xef: "EFI (FAT-12/16/32)",
     0xf2: "DOS secondary",
     0xfd: "Linux RAID",
index afea78b611baebe11cfcf0141c7ab00f29f265c3..6eae9480d78c11572e540551b94686ed1ce43674 100644 (file)
@@ -54,8 +54,6 @@ def partitionObjectsInitialize(pomona):
     # shut down all dm devices
     pomona.id.diskset.closeDevices()
     pomona.id.diskset.stopMdRaid()
-    pomona.id.iscsi.shutdown()
-    pomona.id.zfcp.shutdown()
 
     # clean slate about drives
     isys.flushDriveDict()
@@ -63,9 +61,6 @@ def partitionObjectsInitialize(pomona):
     if pomona.dir == DISPATCH_BACK:
         return
 
-    # ensure zfcp devs are up
-    pomona.id.zfcp.startup()
-
     # pull in the new iscsi drive
     isys.flushDriveDict()
 
@@ -217,10 +212,6 @@ class Partitions:
         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.pomona.id.diskset.refreshDevices()
             self.setFromDisk(self.pomona.id.diskset)
index c036283432cfc20b5544f8dbcd1c5cc4cf6ddfb4..6a264735534a1a5bd803fbe52786f5513d8601a4 100644 (file)
 #                                                                             #
 ###############################################################################
 
+for i in md_mod dm_mod ext3 xfs reiserfs reiser4; do
+       echo "Loading module $i"
+       modprobe $i
+done
+
 echo "Running the Pomona Text Installer..."
 
 python /usr/lib/pomona/installer.py $@
index 041a8379ad03b7a3a4c57ea46be5837e856103aa..ab799503d6397ca8ac8fb306713478eda2fd194b 100644 (file)
@@ -2,16 +2,23 @@
 # partition_text.py: allows the user to choose how to partition their disks
 # in text mode
 #
-# Jeremy Katz <katzj@redhat.com>
+# Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007  Red Hat, Inc.
+# All rights reserved.
 #
-# Copyright 2001-2006 Red Hat, Inc.
+# 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 software may be freely redistributed under the terms of the GNU
-# library public license.
+# 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 Library Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+# 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 os, sys
@@ -19,11 +26,12 @@ import isys
 import string
 import copy
 import parted
-from partitioning import *
+import partitions
 from partedUtils import *
 from partIntfHelpers import *
 from partRequests import *
 from fsset import *
+from raid import availRaidLevels
 from autopart import *
 from snack import *
 from constants import *
@@ -59,8 +67,65 @@ class PartitionWindow:
                 return i
 
     def populate(self):
+        # XXX we really should separate this stuff out into interface
+        # independent bits...
         self.lb.clear()
 
+       # first do LVM
+        lvmrequests = self.partitions.getLVMRequests()
+        if lvmrequests:
+            for vgname in lvmrequests.keys():
+               vgrequest = self.partitions.getRequestByVolumeGroupName(vgname)
+               size = vgrequest.getActualSize(self.partitions, self.diskset)
+               device = "VG %s" % (vgname,)
+                self.lb.append(["%s" % (device,),
+                                "", "", "%dM" %(size),
+                                "VolGroup", ""], str(vgrequest.uniqueID),
+                               [LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT])
+               
+               for lvrequest in lvmrequests[vgname]:
+                   lvdevice = "LV %s" % (lvrequest.logicalVolumeName,)
+                   if lvrequest.fstype and lvrequest.mountpoint:
+                       mntpt = lvrequest.mountpoint
+                   else:
+                       mntpt = ""
+                   lvsize = lvrequest.getActualSize(self.partitions, self.diskset)
+                    ptype = lvrequest.fstype.getName()
+                   self.lb.append(["%s" %(lvdevice),
+                                   "", "", "%dM" %(lvsize),
+                                   "%s" %(ptype), "%s" %(mntpt)], str(lvrequest.uniqueID),
+                                  [LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT])
+
+
+        # next, add the raid partitions
+        self.raidminors = {}
+        raidrequests = self.partitions.getRaidRequests()
+        if raidrequests:
+            for request in raidrequests:
+                if request and request.mountpoint:
+                    mount = request.mountpoint
+                else:
+                    mount = ""
+
+                if request.fstype:
+                    ptype = request.fstype.getName()
+                else:
+                    ptype = _("None")
+
+               try:
+                    self.raidminors[request.raidminor] = True
+                   device = "/dev/md%d" % (request.raidminor,)
+               except:
+                    minor = self._findFirstUnused(self.raidminors)
+                    self.raidminors[minor] = True
+                   device = _("RAID Device %s" %(str(minor)))
+
+                size = request.size
+                self.lb.append(["%s" %(device),
+                                "", "", "%dM" %(size),
+                                "%s" %(ptype), "%s" %(mount)], str(request.uniqueID),
+                               [LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT])
+
         # next, add the drives and partitions to the list
         drives = self.diskset.disks.keys()
         drives.sort()
@@ -74,14 +139,13 @@ class PartitionWindow:
             part = disk.next_partition()
             while part:
                 if part.type & parted.PARTITION_METADATA:
-#                                       print "partition %s has type %d" %(get_partition_name(part), part.type)
+#                    print("partition %s has type %d" %(get_partition_name(part), part.type))
                     part = disk.next_partition(part)
                     continue
-
                 # ignore the tiny < 1 MB partitions (#119479)
                 if getPartSizeMB(part) <= 1.0:
                     if not part.is_active() or not part.get_flag(parted.PARTITION_BOOT):
-                        part = disk.next_partition(part)
+                        part = disk.next_partition(part)                    
                         continue
 
                 device = get_partition_name(part)
@@ -95,11 +159,13 @@ class PartitionWindow:
                     ptype = _("Free space")
                 elif part.type & parted.PARTITION_EXTENDED:
                     ptype = _("Extended")
+                elif part.get_flag(parted.PARTITION_RAID) == 1:
+                    ptype = _("software RAID")
                 elif part.fs_type:
                     if request and request.fstype != None:
                         ptype = request.fstype.getName()
-                    if ptype == "foreign":
-                        ptype = map_foreign_to_fsname(part.native_type)
+                        if ptype == "foreign":
+                            ptype = map_foreign_to_fsname(part.native_type)
                     else:
                         ptype = part.fs_type.name
                 else:
@@ -107,8 +173,8 @@ class PartitionWindow:
                         ptype = request.fstype.getName()
                         if ptype == "foreign":
                             ptype = map_foreign_to_fsname(part.native_type)
-                        else:
-                            ptype = _("None")
+                    else:
+                        ptype = _("None")
 
                 start = (part.geom.start / sectorsPerCyl) + 1
                 end = (part.geom.end / sectorsPerCyl) + 1
@@ -135,12 +201,14 @@ class PartitionWindow:
                                     "%dM" %(size),
                                     "%s" %(ptype),
                                     ""], part,
-                                    [LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT])
-
+                                   [LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT])
+                                    
                 else:
                     dev = devify(get_partition_name(part))
                     # save some space per #90838
-                    if dev.startswith("/dev/"):
+                    if dev.startswith("/dev/iseries/"):
+                        dev = dev[13:]
+                    elif dev.startswith("/dev/"):
                         dev = dev[5:]
                     self.lb.append(["%s%s" %(indent, dev),
                                     "%d" %(start),
@@ -148,39 +216,38 @@ class PartitionWindow:
                                     "%dM" %(size),
                                     "%s" %(ptype),
                                     "%s" %(mount)], part,
-                                    [LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT])
+                                   [LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT])
                 part = disk.next_partition(part)
 
     def refresh(self):
         # XXX need some way to stay at the same place in the list after
         # repopulating
 
-        # 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
+       # 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:
             doPartitioning(self.diskset, self.partitions)
             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")
+           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))
+                   _("Could not allocate requested partitions: %s.") % (msg))
             rc = -1
         except PartitioningWarning, msg:
             rc = ButtonChoiceWindow(self.screen, _("Warning"), _("Warning: %s") %(msg),
-                    buttons = [ (_("Modify Partition"), "modify"), (_("Add anyway"), "add") ])
+                                    buttons = [ (_("Modify Partition"), "modify"), (_("Add anyway"), "add") ])
 
             if rc == "modify":
                 rc = -1
@@ -189,19 +256,20 @@ class PartitionWindow:
                 reqs = self.partitions.getBootableRequest()
                 if reqs:
                     for req in reqs:
-                        req.ignoreBootConstraints = 1
-
+                        req.ignoreBootConstraints = 1                
+                             
         self.populate()
         return rc
 
+
     def fstypeSet(self, obj):
         (current, entry) = obj
         flag = FLAGS_RESET
         if not current.isMountable():
             if entry.value() != _("<Not Applicable>"):
                 self.oldMount = entry.value()
-                entry.set(_("<Not Applicable>"))
-                flag = FLAGS_SET
+            entry.set(_("<Not Applicable>"))
+            flag = FLAGS_SET
         elif entry.value() == _("<Not Applicable>"):
             if self.oldMount:
                 entry.set(self.oldMount)
@@ -230,8 +298,41 @@ class PartitionWindow:
             mount.set(_("<Not Applicable>"))
         return (mount, mountgrid)
 
+    # make the entry for the lv name and it's label
+    def makeLVNameEntry(self, request):
+        lvnamegrid = Grid(2, 1)
+        lvnameLbl = Label(_("Logical Volume Name:"))
+        lvnamegrid.setField(lvnameLbl, 0, 0, (0,0,0,0), anchorLeft = 1)
+        lvn = request.logicalVolumeName
+        if lvn:
+            lvname = Entry(20, lvn)
+        else:
+            lvname = Entry(20, "")
+        lvnamegrid.setField(lvname, 1, 0, anchorRight = 1, growx = 1)
+        if request.preexist:
+            lvname.setFlags(FLAG_DISABLED, FLAGS_SET)
+        return (lvname, lvnamegrid)
+
+    # make the size entry for a logical volume
+    def makeLVSize(self, request):
+        grid = Grid(3, 1)
+        lbl = Label(_("Size (MB):"))
+        grid.setField(lbl, 0, 0, (0,0,0,0), anchorLeft = 1)
+        if request.size:
+            size = Entry(8, "%d" %(request.size,))
+        else:
+            size = Entry(8, "")
+        grid.setField(size, 1, 0, anchorRight = 1, growx = 1)
+#        maxLbl = Label(_("(Max is %s MB") %(maxlv,))
+#        grid.setField(maxLbl, 2, 0, anchorRight = 1)
+        if request.preexist:
+            size.setFlags(FLAG_DISABLED, FLAGS_SET)
+        return (size, grid)
+
+
     # make the list of available filesystems and its label
-    def makeFsList(self, request, usecallback=1, uselabel=1, usetypes=None, ignorefs = None):
+    def makeFsList(self, request, usecallback=1, uselabel=1, usetypes=None,
+                   ignorefs = None):
         subgrid = Grid(1, 2)
         row = 0
         # filesystem type selection
@@ -239,7 +340,7 @@ class PartitionWindow:
             typeLbl = Label(_("File System type:"))
             subgrid.setField(typeLbl, 0, row)
             row = row + 1
-
+            
         fstype = Listbox(height=2, scroll=1)
         types = fileSystemTypeGetTypes()
         if usetypes:
@@ -250,12 +351,14 @@ class PartitionWindow:
         for name in names:
             if not fileSystemTypeGet(name).isSupported():
                 continue
+
             if ignorefs and name in ignorefs:
                 continue
+
             if fileSystemTypeGet(name).isFormattable():
                 fstype.append(name, types[name])
         if request.fstype and request.fstype.getName() in names and \
-                        request.fstype.isFormattable() and request.fstype.isSupported():
+           request.fstype.isFormattable() and request.fstype.isSupported():
             fstype.setCurrent(request.fstype)
         else:
             fstype.setCurrent(fileSystemTypeGetDefault())
@@ -264,6 +367,7 @@ class PartitionWindow:
             fstype.setCallback(self.fstypeSetCB, (fstype, self.mount))
         return (fstype, subgrid)
 
+
     # make the list of drives
     def makeDriveList(self, request):
         subgrid = Grid(1, 2)
@@ -299,12 +403,14 @@ class PartitionWindow:
         sizegrid.setField(size, 1, 0, growx = 1, anchorLeft = 1)
         return (size, sizegrid)
 
+
     def sizeOptionsChange(self, (sizeopts, limitentry)):
         flag = FLAGS_RESET
         if sizeopts.getSelection() != "limit":
             flag = FLAGS_SET
         limitentry.setFlags(FLAG_DISABLED, flag)
 
+
     def makeSizeOptions(self, request):
         # size options
         optiongrid = Grid(2, 3)
@@ -335,15 +441,17 @@ class PartitionWindow:
         self.sizeOptionsChange((sizeopts, limitentry))
         return (sizeopts, limitentry, optiongrid)
 
+
     # the selected cylinder boundary type changed
     def cylOptionsChange(self, (cylopts, end, size)):
         if cylopts.getSelection() == "end":
             end.setFlags(FLAG_DISABLED, FLAGS_RESET)
             size.setFlags(FLAG_DISABLED, FLAGS_SET)
         elif cylopts.getSelection() == "size":
-            end.setFlags(FLAG_DISABLED, FLAGS_SET)
+            end.setFlags(FLAG_DISABLED, FLAGS_SET)            
             size.setFlags(FLAG_DISABLED, FLAGS_RESET)
 
+
     # make the list of cylinder stuff
     def makeCylEntries(self, request):
         subgrid = Grid(2, 4)
@@ -378,9 +486,69 @@ class PartitionWindow:
         endrb.setCallback(self.cylOptionsChange, (cylopts, end, size))
         sizerb.setCallback(self.cylOptionsChange, (cylopts, end, size))
         self.cylOptionsChange((cylopts, end, size))
-
+        
         return (cylopts, start, end, size, subgrid)
 
+    # make the list of VGs
+    def makeVGList(self, request):
+        subgrid = Grid(1, 2)
+        vgLbl = Label(_("Volume Group:"))
+        subgrid.setField(vgLbl, 0, 0)
+        vgs = self.partitions.getLVMVGRequests()
+        if len(vgs) > 3:
+            scroll = 1
+        else:
+            scroll = 0
+        vgBox = Listbox(height=3, scroll=scroll)
+        current = None
+        for vg in vgs:
+            vgBox.append(vg.volumeGroupName, vg)
+            if vg.uniqueID == request.volumeGroup:
+                current = vg
+        if request.volumeGroup is not None:
+            vgBox.setCurrent(current)
+
+        subgrid.setField(vgBox, 0, 1)
+        return (vgBox, subgrid)
+        
+    # make the list of RAID levels
+    def makeRaidList(self, request):
+        subgrid = Grid(1, 2)
+        raidLbl = Label(_("RAID Level:"))
+        subgrid.setField(raidLbl, 0, 0)
+        if len(availRaidLevels) > 3:
+            scroll = 1
+        else:
+            scroll = 0
+        raidBox = Listbox(height=3, scroll=scroll)
+        for level in availRaidLevels:
+            raidBox.append(level, level)
+        if request.raidlevel:
+            raidBox.setCurrent(request.raidlevel)
+        subgrid.setField(raidBox, 0, 1)
+        return (raidBox, subgrid)
+
+
+    # make the list of drives for the RAID
+    def makeRaidDriveList(self, request):
+        subgrid = Grid(1, 2)
+        driveLbl = Label(_("RAID Members:"))
+        subgrid.setField(driveLbl, 0, 0)
+        disks = self.diskset.disks.keys()
+        drivelist = CheckboxTree(height=2, scroll=1)
+        avail = self.partitions.getAvailRaidPartitions(request, self.diskset)
+
+        # XXX
+        if not request.raidmembers:
+            for (part, size, used) in avail:
+                drivelist.append(part, part, 1)
+        else:
+            for (part, size, used) in avail:
+                drivelist.append(part, part, used)
+        subgrid.setField(drivelist, 0, 1)
+        return (drivelist, subgrid)
+
+
     def makeSpareEntry(self, request):
         subgrid = Grid(2, 1)
         label = Label(_("Number of spares?"))
@@ -394,57 +562,55 @@ class PartitionWindow:
         return (entry, subgrid)
 
     def fsOptionsGrid(self, origrequest, newfstype):
-        subgrid = Grid(2, 4)
-        # filesystem type selection
-        srow = 0
-        typeLbl = Label(_("File System Type:"))
-        subgrid.setField(typeLbl, 0, srow, (0,0,0,1), anchorLeft = 1)
-        ptype = origrequest.fstype.getName()
-        if ptype == "foreign":
-            part = get_partition_by_name(self.diskset.disks, origrequest.device)
+       subgrid = Grid(2, 4)
+       # filesystem type selection
+       srow = 0
+       typeLbl = Label(_("File System Type:"))
+       subgrid.setField(typeLbl, 0, srow, (0,0,0,1), anchorLeft = 1)
+       ptype = origrequest.fstype.getName()
+       if ptype == "foreign":
+           part = get_partition_by_name(self.diskset.disks, origrequest.device)
             if part is not None:
                 ptype = map_foreign_to_fsname(part.native_type)
             else:
                 pytype = _("Foreign")
-        type = Label(ptype)
-        subgrid.setField(type, 1, srow, (0,0,0,1), anchorRight = 1)
-        srow = srow +1
-        if origrequest.type != REQUEST_NEW and origrequest.fslabel:
-            fsLbl = Label(_("File System Label:"))
-            subgrid.setField(fsLbl, 0, srow, (0,0,0,1), anchorLeft = 1)
-            label = Label(origrequest.fslabel)
-            subgrid.setField(label, 1, srow, (0,0,0,1), anchorRight = 1)
-            srow = srow + 1
-
-        sizeLbl = Label(_("Size (MB):"))
-        subgrid.setField(sizeLbl, 0, srow, (0,0,0,1), anchorLeft = 1)
-        size = Label("%s" %(int(origrequest.size)))
-        subgrid.setField(size, 1, srow, (0,0,0,1), anchorRight = 1)
-        srow = srow + 1
-        tmpLbl = Label(_("File System Option:"))
-        subgrid.setField(tmpLbl, 0, srow, (0,0,0,1), anchorLeft = 1)
-        if origrequest.format:
-            fsoptLbl = Label(_("Format as %s") % (newfstype.getName()))
-        elif origrequest.migrate:
-            fsoptLbl = Label(_("Migrate to %s") %(newfstype.getName()))
-        else:
-            fsoptLbl = Label(_("Leave unchanged"))
-        subgrid.setField(fsoptLbl, 1, srow, (0,0,0,1), anchorLeft = 1)
-
-        return (subgrid, fsoptLbl, type)
-
-    def fsOptionsDialog(self, origrequest, format, migrate, newfstype, badblocks,
-            showbadblocks=0):
-
-        def formatChanged((formatrb, badblocksCB)):
+       type = Label(ptype)
+       subgrid.setField(type, 1, srow, (0,0,0,1), anchorRight = 1)
+       srow = srow +1
+       if origrequest.type != REQUEST_NEW and origrequest.fslabel:
+           fsLbl = Label(_("File System Label:"))
+           subgrid.setField(fsLbl, 0, srow, (0,0,0,1), anchorLeft = 1)
+           label = Label(origrequest.fslabel)
+           subgrid.setField(label, 1, srow, (0,0,0,1), anchorRight = 1)
+           srow = srow + 1
+
+       sizeLbl = Label(_("Size (MB):"))
+       subgrid.setField(sizeLbl, 0, srow, (0,0,0,1), anchorLeft = 1)
+       size = Label("%s" %(int(origrequest.size)))
+       subgrid.setField(size, 1, srow, (0,0,0,1), anchorRight = 1)
+       srow = srow + 1
+       tmpLbl = Label(_("File System Option:"))
+       subgrid.setField(tmpLbl, 0, srow, (0,0,0,1), anchorLeft = 1)
+       if origrequest.format:
+           fsoptLbl = Label(_("Format as %s") % (newfstype.getName()))
+       elif origrequest.migrate:
+           fsoptLbl = Label(_("Migrate to %s") %(newfstype.getName()))
+       else:
+           fsoptLbl = Label(_("Leave unchanged"))
+       subgrid.setField(fsoptLbl, 1, srow, (0,0,0,1), anchorLeft = 1)
+
+       return (subgrid, fsoptLbl, type)
+       
+
+    def fsOptionsDialog(self, origrequest, format, migrate, newfstype):
+
+        def formatChanged((formatrb)):
             flag = FLAGS_SET
             if formatrb.selected():
                 flag = FLAGS_RESET
-            if badblocksCB:
-                badblocksCB.setFlags(FLAG_DISABLED, flag)
 
         poplevel = GridFormHelp(self.screen, _("File System Options"),
-                                               "fsoption", 1, 6)
+                                "fsoption", 1, 6)
         row = 0
         poplevel.add(TextboxReflowed(40, _("Please choose how you would "
                                            "like to prepare the file system "
@@ -453,30 +619,26 @@ class PartitionWindow:
         subgrid = Grid(2, 5)
         srow = 0
 
-        if showbadblocks:
-            badblocksCB = Checkbox(_("Check for bad blocks"))
-        else:
-            badblocksCB = None
-
         noformatrb = SingleRadioButton(_("Leave unchanged (preserve data)"),
                                        None, not format and not migrate)
         subgrid.setField(noformatrb, 0, srow, (0,0,0,1),anchorLeft = 1)
-
+        
         srow = srow + 1
         if format:
             forflag = 1
         else:
             forflag = 0
         formatrb = SingleRadioButton(_("Format as:"), noformatrb, forflag)
-        formatrb.setCallback(formatChanged, (formatrb, badblocksCB))
-        noformatrb.setCallback(formatChanged, (formatrb, badblocksCB))
-
+        formatrb.setCallback(formatChanged, (formatrb))
+        noformatrb.setCallback(formatChanged, (formatrb))
+       
         subgrid.setField(formatrb, 0, srow, (0,0,0,1), anchorLeft = 1)
 
-        (fortype, forgrid) = self.makeFsList(origrequest, usecallback = 0, uselabel = 0)
+        (fortype, forgrid) = self.makeFsList(origrequest, usecallback = 0,
+                                             uselabel = 0)
         if newfstype and newfstype.isFormattable() and \
-                        newfstype.getName() in fileSystemTypeGetTypes().keys() and \
-                        newfstype.isSupported():
+           newfstype.getName() in fileSystemTypeGetTypes().keys() and \
+           newfstype.isSupported():
             fortype.setCurrent(newfstype)
         subgrid.setField(forgrid, 1, srow, (0,0,0,1))
 
@@ -487,17 +649,18 @@ class PartitionWindow:
             else:
                 migflag = 0
             migraterb = SingleRadioButton(_("Migrate to:"), formatrb, migflag)
-            migraterb.setCallback(formatChanged, (formatrb, badblocksCB))
+            migraterb.setCallback(formatChanged, (formatrb))
             subgrid.setField(migraterb, 0, srow, (0,0,0,1), anchorLeft = 1)
 
             migtypes = origrequest.origfstype.getMigratableFSTargets()
 
             (migtype, miggrid) = self.makeFsList(origrequest, usecallback = 0,
-                                                 uselabel = 0, usetypes = migtypes)
+                                                 uselabel = 0,
+                                                 usetypes = migtypes)
 
             if newfstype and newfstype.getName() in migtypes:
                 migtype.setCurrent(newfstype)
-                subgrid.setField(miggrid, 1, srow, (0,0,0,1))
+            subgrid.setField(miggrid, 1, srow, (0,0,0,1))
         else:
             migraterb = None
 
@@ -505,23 +668,17 @@ class PartitionWindow:
 
         row = row + 1
 
-        if badblocksCB:
-            poplevel.add(badblocksCB, 0, row, (0,1,0,1))
-            if badblocks:
-                badblocksCB.setValue("*")
-                row = row + 1
-
-        formatChanged((formatrb, badblocksCB))
+        formatChanged((formatrb))
 
         popbb = ButtonBar(self.screen, (TEXT_OK_BUTTON, TEXT_CANCEL_BUTTON))
-        poplevel.add(popbb, 0, row, (0,0,0,0), growx = 1)
+        poplevel.add(popbb, 0, row, (0,0,0,0), growx = 1)        
 
         while 1:
             res = poplevel.run()
 
             if popbb.buttonPressed(res) == 'cancel':
                 self.screen.popWindow()
-                return (format, migrate, newfstype, badblocks)
+                return (format, migrate, newfstype)
 
             if noformatrb.selected():
                 format = 0
@@ -538,12 +695,7 @@ class PartitionWindow:
 
             self.screen.popWindow()
 
-            if badblocksCB:
-                badblockstate = badblocksCB.selected()
-            else:
-                badblockstate = 0
-
-            return (format, migrate, newfstype, badblockstate)
+            return (format, migrate, newfstype)
 
     def shutdownUI(self):
         # XXX remove parted object refs
@@ -555,7 +707,7 @@ class PartitionWindow:
     # isNew implies that this request has never been successfully used before
     def editPartitionRequest(self, origrequest, isNew = 0):
         self.oldMount = None
-
+        
         poplevel = GridFormHelp(self.screen,_("Add Partition"),"addpart", 1, 6)
 
         # mount point entry
@@ -594,7 +746,6 @@ class PartitionWindow:
                 (cylopts, start, end, size, cylgrid) = self.makeCylEntries(origrequest)
                 poplevel.add(cylgrid, 0, row, (0,1,0,0))
 
-
             # primary
             # XXX need to see if cylinder range is in extended or not
             row = row + 1
@@ -604,33 +755,36 @@ class PartitionWindow:
                 poplevel.add(primary, 0, row, (0,1,0,0))
                 row = row + 1
 
-            badblocksCB = Checkbox(_("Check for bad blocks"))
-            poplevel.add(badblocksCB, 0, row)
-            if origrequest.badblocks:
-                badblocksCB.setValue("*")
-
             fsoptLbl = None
 
-        elif origrequest.type == REQUEST_PREEXIST and origrequest.fstype:
+       elif origrequest.type == REQUEST_VG:
+           self.intf.messageWindow(_("Not Supported"),
+                                   _("You can only edit LVM Volume Groups "
+                                     "in the graphical installer."))
+           return
+
+        elif (origrequest.type == REQUEST_LV or origrequest.type == REQUEST_PREEXIST) and origrequest.fstype:
+
             # set some defaults
             format = origrequest.format
             migrate = origrequest.migrate
             newfstype = origrequest.fstype
-            badblocks = origrequest.badblocks
 
             (subgrid, fsoptLbl, fstypeLbl) = self.fsOptionsGrid(origrequest, newfstype)
             poplevel.add(subgrid, 0, row, (0,1,0,0))
 
+
         row = row + 1
         if origrequest.type == REQUEST_NEW or origrequest.getProtected():
             popbb = ButtonBar(self.screen, (TEXT_OK_BUTTON, TEXT_CANCEL_BUTTON))
         else:
             popbb = ButtonBar(self.screen, (TEXT_OK_BUTTON,
-                             (_("File System Options"), "fsopts"),
-                              TEXT_CANCEL_BUTTON))
+                                            (_("File System Options"), "fsopts"),
+                                            TEXT_CANCEL_BUTTON))
         poplevel.add(popbb, 0, row, (0,1,0,0), growx = 1)
 
         while 1:
+            
             res = poplevel.run()
 
             # if the user hit cancel, do nothing
@@ -639,11 +793,7 @@ class PartitionWindow:
                 return
 
             if popbb.buttonPressed(res) == 'fsopts':
-                # we do not show the badblock option any longer as it is
-                # not supported.
-                showbad = 0
-                (format, migrate, newfstype, badblocks) = self.fsOptionsDialog(origrequest,
-                                format, migrate, newfstype, badblocks, showbadblocks = showbad)
+                (format, migrate, newfstype) = self.fsOptionsDialog(origrequest, format, migrate, newfstype)
                 self.fstypeSet((newfstype, self.mount))
                 fstypeLbl.setText(newfstype.getName())
 
@@ -651,7 +801,7 @@ class PartitionWindow:
                     if format:
                         fsoptLbl.setText(_("Format as %s") % (newfstype.getName()))
                     elif migrate:
-                        fsoptLbl.setText(_("Migrate to %s") % (newfstype.getName()))
+                        fsoptLbl.setText(_("Migrate to %s") %(newfstype.getName()))
                     else:
                         fsoptLbl.setText(_("Leave unchanged"))
 
@@ -661,7 +811,7 @@ class PartitionWindow:
                 filesystem = fstype.current()
 
                 if primary.selected():
-                    primonly = 1
+                    primonly = True
                 else:
                     primonly = None
 
@@ -671,14 +821,9 @@ class PartitionWindow:
                     request.mountpoint = self.mount.value()
                 else:
                     request.mountpoint = None
-                request.format = 1
+                request.format = True
                 request.primary = primonly
 
-                if badblocksCB is not None:
-                    request.badblocks = badblocksCB.selected()
-                else:
-                    request.badblocks = 0
-
                 if origrequest.start == None:
                     if invalidInteger(size.value()):
                         self.intf.messageWindow(_("Invalid Entry for Partition Size"),
@@ -690,17 +835,16 @@ class PartitionWindow:
                     if growtype == "fixed":
                         grow = None
                     else:
-                        grow = 1
+                        grow = True
                     if growtype == "limit":
                         if invalidInteger(limitentry.value()):
                             self.intf.messageWindow(_("Invalid Entry for Maximum Size"),
-                                    invalidInteger(limitentry.value()))
+                                           invalidInteger(limitentry.value()))
                             continue
-
+                            
                         maxsize = int(limitentry.value())
                     else:
                         maxsize = None
-
                     request.grow = grow
                     request.maxSizeMB = maxsize
 
@@ -709,68 +853,64 @@ class PartitionWindow:
                     else:
                         allowdrives = []
                         for i in self.drivelist.getSelection():
-                            allowdrives.append(i)
+                            allowdrives.append(i) 
                     request.drive = allowdrives
                 else:
                     if invalidInteger(start.value()):
                         self.intf.messageWindow(_("Invalid Entry for Starting Cylinder"),
-                                                invalidInteger(start.value()))
+                                           invalidInteger(start.value()))
                         continue
 
                     request.start = int(start.value())
 
-                    if badblocksCB is not None:
-                        request.badblocks = badblocksCB.selected()
-                    else:
-                        request.badblocks = 0
-
                     cyltype = cylopts.getSelection()
                     if cyltype == "end":
                         if invalidInteger(end.value()):
                             self.intf.messageWindow(_("Invalid Entry for End Cylinder"),
-                                                    invalidInteger(end.value()))
+                                           invalidInteger(end.value()))
                             continue
-
+                        
                         request.end = int(end.value())
                         request.size = None
                     elif cyltype == "size":
                         if invalidInteger(size.value()):
                             self.intf.messageWindow(_("Invalid Entry for Partition Size"),
-                                                    invalidInteger(size.value()))
+                                           invalidInteger(size.value()))
                             continue
                         request.end = None
                         request.size = int(size.value())
                     else: # can't ever get here
                         raise RuntimeError, "Selected a way of partitioning by cylinder that's not supported"
-
+                    
                 err = request.sanityCheckRequest(self.partitions)
                 if err:
-                    self.intf.messageWindow(_("Error With Request"), "%s" % (err))
+                    self.intf.messageWindow(_("Error With Request"),
+                                            "%s" % (err))
                     continue
             else:
                 request = copy.copy(origrequest)
 
-                if request.type == REQUEST_PREEXIST:
+                if request.type == REQUEST_PREEXIST or request.type == REQUEST_LV:
                     request.fstype = newfstype
-
+                    
                 if request.fstype.isMountable():
                     request.mountpoint = self.mount.value()
                 else:
                     request.mountpoint = None
 
-                if request.type == REQUEST_PREEXIST:
+                if request.type == REQUEST_PREEXIST or request.type == REQUEST_LV:
                     request.format = format
                     request.migrate = migrate
                     request.fstype = newfstype
-                    request.badblocks = badblocks
 
                 err = request.sanityCheckRequest(self.partitions)
                 if err:
-                    self.intf.messageWindow(_("Error With Request"), "%s" % (err))
+                    self.intf.messageWindow(_("Error With Request"),
+                                            "%s" % (err))
                     continue
 
                 if (not request.format and request.mountpoint
-                                and request.formatByDefault()):
+                    and request.formatByDefault()):
                     if not queryNoFormatPreExisting(self.intf):
                         continue
 
@@ -794,15 +934,449 @@ class PartitionWindow:
         self.shutdownUI()
         self.screen.popWindow()
 
+    # isNew implies that this request has never been successfully used before
+    def editRaidRequest(self, raidrequest, isNew = 0):
+       preexist = raidrequest and raidrequest.preexist
+       if preexist:
+           tmpstr = _("Edit RAID Device")
+       else:
+           tmpstr = _("Make RAID Device")
+        poplevel = GridFormHelp(self.screen, tmpstr, "makeraid", 1, 6)
+
+        # mount point entry
+        row = 0
+        (self.mount, mountgrid) = self.makeMountEntry(raidrequest)
+        poplevel.add(mountgrid, 0, row)
+        row = row + 1
+
+       # initialize holder for temporary mount point value
+       self.oldMount = None
+
+       if preexist:
+            # set some defaults
+            format = raidrequest.format
+            migrate = raidrequest.migrate
+            newfstype = raidrequest.fstype
+
+            (subgrid, fsoptLbl, fstypeLbl) = self.fsOptionsGrid(raidrequest, newfstype)
+            poplevel.add(subgrid, 0, row, (0,1,0,0))
+           self.drivelist = None
+       else:
+           subgrid = Grid(2, 1)
+           (fstype, fsgrid) = self.makeFsList(raidrequest, ignorefs = ["software RAID", "PPC PReP Boot", "Apple Bootstrap"])
+           subgrid.setField(fsgrid, 0, 0, anchorLeft = 1, anchorTop=1)
+           (raidtype, raidgrid) = self.makeRaidList(raidrequest)
+           subgrid.setField(raidgrid, 1, 0, (2,0,0,0), anchorRight=1, anchorTop=1)
+           poplevel.add(subgrid, 0, row, (0,1,0,0))
+
+           row = row + 1
+           drivegrid = Grid(2, 1)
+
+           #Let's see if we have any RAID partitions to make a RAID device with
+           avail = self.partitions.getAvailRaidPartitions(raidrequest, self.diskset)
+
+           #If we don't, then tell the user that none exist
+           if len(avail) < 2:
+               ButtonChoiceWindow (self.screen, _("No RAID partitions"),
+                                   _("At least two software RAID partitions are needed."),
+                                   [ TEXT_OK_BUTTON ])
+               return
+
+           (self.drivelist, drivesubgrid) = self.makeRaidDriveList(raidrequest)
+           drivegrid.setField(drivesubgrid, 0, 0, (0,0,4,0), anchorLeft = 1, anchorTop = 1)
+
+           miscgrid = Grid(1, 2)
+           (spares, sparegrid) = self.makeSpareEntry(raidrequest)
+           miscgrid.setField(sparegrid, 0, 0, anchorRight=1, anchorTop=1)
+
+           if raidrequest.fstype and raidrequest.fstype.isFormattable():
+               format = Checkbox(_("Format partition?"))
+               miscgrid.setField(format, 0, 1)
+           else:
+               format = None
+
+           if raidrequest.format == 1 or raidrequest.format == None:
+               format.setValue("*")
+
+           drivegrid.setField(miscgrid, 1, 0, anchorTop=1)
+           poplevel.add(drivegrid, 0, row, (0,1,0,0))        
+
+        row = row + 1
+       if preexist:
+            popbb = ButtonBar(self.screen, (TEXT_OK_BUTTON,
+                                            (_("File System Options"), "fsopts"),
+                                            TEXT_CANCEL_BUTTON))
+       else:
+           popbb = ButtonBar(self.screen, (TEXT_OK_BUTTON,TEXT_CANCEL_BUTTON))
+        poplevel.add(popbb, 0, row, (0,1,0,0), growx = 1)        
+
+        while 1:
+            res = poplevel.run()
+
+            if popbb.buttonPressed(res) == 'cancel':
+                self.screen.popWindow()
+                return
+
+            if popbb.buttonPressed(res) == 'fsopts':
+                (format, migrate, newfstype) = self.fsOptionsDialog(raidrequest, format, migrate, newfstype)
+                self.fstypeSet((newfstype, self.mount))
+                fstypeLbl.setText(newfstype.getName())
+
+                if fsoptLbl:
+                    if format:
+                        fsoptLbl.setText(_("Format as %s") % (newfstype.getName()))
+                    elif migrate:
+                        fsoptLbl.setText(_("Migrate to %s") %(newfstype.getName()))
+                    else:
+                        fsoptLbl.setText(_("Leave unchanged"))
+                
+                continue
+
+            request = copy.copy(raidrequest)
+
+           if not preexist:
+               request.fstype = fstype.current()
+           else:
+               request.fstype = newfstype
+
+            if request.fstype.isMountable():
+                request.mountpoint = self.mount.value()
+            else:
+                request.mountpoint = None
+
+           if not preexist:
+               raidmembers = []
+               for drive in self.drivelist.getSelection():
+                   id = self.partitions.getRequestByDeviceName(drive).uniqueID
+                   raidmembers.append(id)
+
+               request.raidmembers = raidmembers
+               if invalidInteger(spares.value()):
+                   self.intf.messageWindow(_("Invalid Entry for RAID Spares"),
+                                           invalidInteger(spares.value()))
+                   continue
+
+               request.raidspares = int(spares.value())
+               request.raidlevel = raidtype.current()
+                request.raidminor = self._findFirstUnused(self.raidminors)
+                self.raidminors[request.raidminor] = True
+
+               if format:
+                   request.format = format.selected()
+               else:
+                   request.format = 0
+
+               if request.raidlevel == "RAID0" and request.raidspares > 0:
+                   self.intf.messageWindow(_("Too many spares"),
+                                             _("You may not use any spares "
+                                             "with a RAID0 array."))
+                   continue
+           else:
+               request.format = format
+               request.migrate = migrate
+               request.fstype = newfstype
+
+            err = request.sanityCheckRequest(self.partitions)
+            if err:
+                self.intf.messageWindow(_("Error With Request"),
+                                        "%s" % (err))
+                continue
+
+            if not isNew:
+                self.partitions.removeRequest(raidrequest)
+
+            self.partitions.addRequest(request)
+            
+            if self.refresh():
+                # how can this fail?  well, if it does, do the remove new,
+                # add old back in dance
+                self.partitions.removeRequest(request)
+                if not isNew:
+                    self.partitions.addRequest(raidrequest)
+                if self.refresh():
+                    raise RuntimeError, "Returning partitions to state prior to RAID edit failed"
+            else:
+                break
+
+            break
+
+        # clean up
+        self.shutdownUI()
+        self.screen.popWindow()
+        
+    # isNew implies that this request has never been successfully used before
+    def editLVRequest(self, lvrequest, isNew = 0):
+       preexist = lvrequest and lvrequest.preexist
+       if preexist:
+           tmpstr = _("Edit Logical Volume")
+       else:
+           tmpstr = _("Make Logical Volume")
+        self.drivelist = None            
+        poplevel = GridFormHelp(self.screen, tmpstr, "makelv", 1, 8)
+
+        # mount point entry
+        row = 0
+        (self.mount, mountgrid) = self.makeMountEntry(lvrequest)
+        poplevel.add(mountgrid, 0, row)
+        row = row + 1
+
+        (self.lvname, lvgrid) = self.makeLVNameEntry(lvrequest)
+        poplevel.add(lvgrid, 0, row)
+        row = row + 1
+
+        (lvsize, lvsizegrid) = self.makeLVSize(lvrequest)
+        poplevel.add(lvsizegrid, 0, row)
+        row = row + 1
+
+       # initialize holder for temporary mount point value
+       self.oldMount = None
+
+       if preexist:
+            # set some defaults
+            format = lvrequest.format
+            migrate = lvrequest.migrate
+            newfstype = lvrequest.fstype
+
+            (subgrid, fsoptLbl, fstypeLbl) = self.fsOptionsGrid(lvrequest, newfstype)
+            poplevel.add(subgrid, 0, row, (0,1,0,0))
+           self.drivelist = None
+       else:
+           subgrid = Grid(2, 1)
+           (fstype, fsgrid) = self.makeFsList(lvrequest, ignorefs = ["software RAID", "efi", "PPC PReP Boot", "Apple Bootstrap"])
+           subgrid.setField(fsgrid, 0, 0, anchorLeft = 1, anchorTop=1)
+
+            vgs = self.partitions.getLVMVGRequests()
+            if len(vgs) < 1:
+                ButtonChoiceWindow (self.screen, _("No Volume Groups"),
+                                    _("No volume groups exist in which to create "
+                                      "a logical volume"),[ TEXT_OK_BUTTON ])
+                return
+
+            (vgtype, vggrid) = self.makeVGList(lvrequest)
+           subgrid.setField(vggrid, 1, 0, (2,0,0,0), anchorRight=1, anchorTop=1)
+           poplevel.add(subgrid, 0, row, (0,1,0,0))
+
+           row = row + 1
+
+           miscgrid = Grid(1, 3)
+
+           if lvrequest.fstype and lvrequest.fstype.isFormattable():
+               format = Checkbox(_("Format partition?"))
+               miscgrid.setField(format, 0, 1)
+           else:
+               format = None
+
+           if lvrequest.format == 1 or lvrequest.format == None:
+               format.setValue("*")
+
+           poplevel.add(miscgrid, 0, row, (0,1,0,0))        
+
+        row = row + 1
+       if preexist:
+            popbb = ButtonBar(self.screen, (TEXT_OK_BUTTON,
+                                            (_("File System Options"), "fsopts"),
+                                            TEXT_CANCEL_BUTTON))
+       else:
+           popbb = ButtonBar(self.screen, (TEXT_OK_BUTTON,TEXT_CANCEL_BUTTON))
+        poplevel.add(popbb, 0, row, (0,1,0,0), growx = 1)        
+
+        while 1:
+            res = poplevel.run()
+
+            if popbb.buttonPressed(res) == 'cancel':
+                self.screen.popWindow()
+                return
+
+            if popbb.buttonPressed(res) == 'fsopts':
+                (format, migrate, newfstype) = self.fsOptionsDialog(lvrequest, format, migrate, newfstype)
+                self.fstypeSet((newfstype, self.mount))
+                fstypeLbl.setText(newfstype.getName())
+
+                if fsoptLbl:
+                    if format:
+                        fsoptLbl.setText(_("Format as %s") % (newfstype.getName()))
+                    elif migrate:
+                        fsoptLbl.setText(_("Migrate to %s") %(newfstype.getName()))
+                    else:
+                        fsoptLbl.setText(_("Leave unchanged"))
+
+                continue
+
+            request = copy.copy(lvrequest)
+
+           if not preexist:
+               request.fstype = fstype.current()
+           else:
+               request.fstype = newfstype
+
+            if request.fstype.isMountable():
+                request.mountpoint = self.mount.value()
+            else:
+                request.mountpoint = None
+
+           if not preexist:
+               if format:
+                   request.format = format.selected()
+               else:
+                   request.format = 0
+
+                # set the vg
+                vgreq = vgtype.current()
+                request.volumeGroup = vgreq.uniqueID
+
+                if vgreq is None:
+                    raise RuntimeError, "Somehow ended up with a None volume group!"
+
+                # get the lv name, check for a "valid" name
+                lvn = self.lvname.value().strip()
+                err = sanityCheckLogicalVolumeName(lvn)
+                if err:
+                    self.intf.messageWindow(_("Illegal Logical Volume Name"), err, custom_icon="error")
+                    
+                    continue
+
+                # make sure we don't have an LV in this volume group by
+                # this name already
+                used = 0
+                origlvname = lvrequest.logicalVolumeName
+                for lv in self.partitions.getLVMLVRequests():
+                    if origlvname and lvn == origlvname:
+                        break
+                    if ((lv.logicalVolumeName == lvn) and
+                        (lv.volumeGroup == vgreq.uniqueID)):
+                        used = 1
+                        break
+
+                if used:
+                    self.intf.messageWindow(_("Illegal logical volume name"),
+                                            _("The logical volume name \"%s\" "
+                                              "is already in use. Please "
+                                              "pick another.") % (lvn,),
+                                            custom_icon="error")
+                    continue
+                    
+                request.logicalVolumeName = lvn
+
+                try:
+                    size = int(lvsize.value().strip())
+                except:
+                    self.intf.messageWindow(_("Illegal size"),
+                                            _("The requested size as entered is "
+                                              "not a valid number greater "
+                                              "than 0."), custom_icon="error")
+                    continue
+                                            
+                pesize = vgreq.pesize
+                size = lvm.clampLVSizeRequest(size, pesize, roundup=1)
+
+                maxlv = lvm.getMaxLVSize(pesize)
+                if size > lvm.getMaxLVSize(pesize):
+                    self.intf.messageWindow(_("Not enough space"),
+                                            _("The current requested size "
+                                              "(%10.2f MB) is larger than the "
+                                              "maximum logical volume "
+                                              "size (%10.2f MB). ") % (size,
+                                                                       maxlv),
+                                            custom_icon="error")
+                    continue
+                    
+                vgsize = vgreq.getActualSize(self.partitions, self.diskset)
+                avail = vgsize
+                for req in self.partitions.requests:
+                    if ((req.type == REQUEST_LV) and 
+                        (req.volumeGroup == vgreq.uniqueID)):
+                        avail -= req.size
+                if lvrequest.size:
+                    avail += lvrequest.size
+
+                if size > avail:
+                    self.intf.messageWindow(_("Not enough space"),
+                                            _("The current requested size "
+                                              "(%10.2f MB) is larger than "
+                                              "the available size in "
+                                              "the volume group "
+                                              "(%10.2f MB).") %(size, avail),
+                                            custom_icon="error")
+                    continue
+
+                request.size = size
+                request.grow = 0
+                request.dev = None
+           else:
+               request.format = format
+               request.migrate = migrate
+               request.fstype = newfstype
+
+            err = request.sanityCheckRequest(self.partitions)
+            if err:
+                self.intf.messageWindow(_("Error With Request"),
+                                        "%s" % (err))
+                continue
+
+            if not isNew:
+                self.partitions.removeRequest(lvrequest)
+
+            self.partitions.addRequest(request)
+
+            if self.refresh():
+                # how can this fail?  well, if it does, do the remove new,
+                # add old back in dance
+                self.partitions.removeRequest(request)
+                if not isNew:
+                    self.partitions.addRequest(lvrequest)
+                if self.refresh():
+                    raise RuntimeError, "Returning partitions to state prior to RAID edit failed"
+            else:
+                break
+
+            break
+
+        # clean up
+        self.shutdownUI()
+        self.screen.popWindow()
+
     def newCb(self):
-        request = NewPartitionSpec(fileSystemTypeGetDefault(), 1)
-        self.editPartitionRequest(request, isNew = 1)
+        hasvg = 1
+        dolv = 0
+        for request in self.partitions.requests:
+            if request.type == REQUEST_VG:
+                hasvg = 1
+                break
+        if hasvg:
+            rc = ListboxChoiceWindow(self.screen,
+                                     _("New Partition or Logical Volume?"),
+                                     _("Would you like to create a new "
+                                       "partition or a new logical volume?"),
+                                     [ _("partition"), _("logical volume") ],
+                                     [ TEXT_OK_BUTTON, TEXT_CANCEL_BUTTON ],
+                                     width = 30, scroll = 0, height = 2)
+            (button, choice) = rc
+            if button == TEXT_CANCEL_CHECK:
+                return
+            if choice == 1:
+                dolv = 1
+
+        if not dolv:
+            request = NewPartitionSpec(fileSystemTypeGetDefault(), 1)
+            self.editPartitionRequest(request, isNew = 1)
+        else:
+            request = LogicalVolumeRequestSpec(fileSystemTypeGetDefault(),
+                                               size=1)
+            self.editLVRequest(request, isNew = 1)
+
+    def makeraidCb(self):
+        request = RaidRequestSpec(fileSystemTypeGetDefault())
+        self.editRaidRequest(request, isNew = 1)
 
     def editCb(self):
         part = self.lb.current()
         (type, request) = doEditPartitionByRequest(self.intf, self.partitions, part)
         if request:
-            if type == "NEW":
+            if type == "RAID":
+                self.editRaidRequest(request)
+            elif type == "LVMLV":
+                self.editLVRequest(request)
+            elif type == "NEW":
                 self.editPartitionRequest(request, isNew = 1)
             else:
                 self.editPartitionRequest(request)
@@ -810,15 +1384,19 @@ class PartitionWindow:
     def deleteCb(self):
         partition = self.lb.current()
 
+        req = self.partitions.getRequestByID(partition)
+        if req and isinstance(req, RaidRequestSpec):
+            del(self.raidminors[req.raidminor])
+
         if doDeletePartitionByRequest(self.intf, self.partitions, partition):
             self.refresh()
 
     def resetCb(self):
         if not confirmResetPartitionState(self.intf):
             return
-
+        
         self.diskset.refreshDevices()
-        self.partitions.setFromDisk(self.diskset)
+        self.partitions.setFromDisk(self.diskset)        
         self.populate()
 
     def shutdownMainUI(self):
@@ -848,8 +1426,9 @@ class PartitionWindow:
         self.bb = ButtonBar (screen, ((_("New"), "new", "F2"),
                                       (_("Edit"), "edit", "F3"),
                                       (_("Delete"), "delete", "F4"),
+                                      (_("RAID"), "raid", "F11"),
                                       TEXT_OK_BUTTON, TEXT_BACK_BUTTON))
-
+        
         screen.pushHelpLine( _("    F1-Help     F2-New      F3-Edit   F4-Delete    F5-Reset    F12-OK        "))
 
         self.g.add(self.bb, 0, 2, (0, 1, 0, 0))
@@ -859,13 +1438,15 @@ class PartitionWindow:
         while 1:
             rc = self.g.run()
             res = self.bb.buttonPressed(rc)
-
+            
             if res == "new":
                 self.newCb()
             elif res == "edit" or rc == self.lb.listbox: # XXX better way?
                 self.editCb()
             elif res == "delete":
                 self.deleteCb()
+            elif res == "raid":
+                self.makeraidCb()
             elif res == "reset" or rc == "F5":
                 self.resetCb()
             elif res == TEXT_BACK_CHECK:
@@ -874,26 +1455,27 @@ class PartitionWindow:
 
                 self.diskset.refreshDevices()
                 self.partitions.setFromDisk(self.diskset)
-
+                
                 screen.popHelpLine()
                 screen.popWindow()
                 return INSTALL_BACK
             else:
                 if not self.partitions.getRequestByMountPoint("/"):
                     self.intf.messageWindow(_("No Root Partition"),
-                                            _("Installation requires a / partition."))
+                        _("Installation requires a / partition."))
                     continue
-
+                
                 (errors, warnings) = self.partitions.sanityCheckAllRequests(self.diskset)
                 rc = partitionSanityErrors(self.intf, errors)
                 if rc != 1:
                     continue
-
+        
                 rc = partitionSanityWarnings(self.intf, warnings)
                 if rc != 1:
                     continue
 
-                warnings = getPreExistFormatWarnings(self.partitions, self.diskset)
+                warnings = getPreExistFormatWarnings(self.partitions,
+                                                     self.diskset)
                 rc = partitionPreExistFormatWarnings(self.intf, warnings)
                 if rc != 1:
                     continue
@@ -902,12 +1484,14 @@ class PartitionWindow:
                 self.shutdownMainUI()
 
                 screen.popHelpLine()
-                screen.popWindow()
+                screen.popWindow()                
                 return INSTALL_OK
-
+        
 class PartitionTypeWindow:
     def typeboxChange(self, (typebox, drivelist)):
         flag = FLAGS_RESET
+        if typebox.current() == CLEARPART_TYPE_NONE:
+            flag = FLAGS_SET
         # XXX need a way to disable the checkbox tree
 
     def clearDrivelist(self):
@@ -922,11 +1506,7 @@ class PartitionTypeWindow:
         while 1:
             g = GridFormHelp(screen, _("Partitioning Type"), "autopart", 1, 6)
 
-            txt = TextboxReflowed(65, _("Installation requires partitioning "
-                                        "of your hard drive.  The default "
-                                        "layout is reasonable for most "
-                                        "users.  You can either choose "
-                                        "to use this or create your own."))
+            txt = TextboxReflowed(65, _("Installation requires partitioning of your hard drive.  The default layout is suitable for most users.  Select what space to use and which drives to use as the install target. You can also choose to create your own custom layout."))
             g.add(txt, 0, 0, (0, 0, 0, 0))
 
             opts = ((_("Remove all partitions on selected drives and create default layout"), CLEARPART_TYPE_ALL),
@@ -938,14 +1518,15 @@ class PartitionTypeWindow:
             if pomona.dispatch.stepInSkipList("autopartitionexecute"):
                 typebox.setCurrent(-1)
             else:
-                typebox.setCurrent(pomona.id.partitions.autoClearPartType)
+                typebox.setCurrent(pomona.id.partitions.autoClearPartType)        
 
             g.add(typebox, 0, 1, (0, 1, 0, 0))
 
             # list of drives to select which to clear
             subgrid = Grid(1, 2)
             subgrid.setField(TextboxReflowed(55, _("Which drive(s) do you want to "
-                                                   "use for this installation?")), 0, 0)
+                                                   "use for this installation?")),
+                             0, 0)
             drivelist = CheckboxTree(height=2, scroll=1)
             subgrid.setField(drivelist, 0, 1)
             g.add(subgrid, 0, 2, (0, 1, 0, 0))
@@ -953,11 +1534,11 @@ class PartitionTypeWindow:
             bb = ButtonBar(screen, [ TEXT_OK_BUTTON, TEXT_BACK_BUTTON ])
             g.add(bb, 0, 5, (0,1,0,0))
 
-            typebox.setCallback(self.typeboxChange, (typebox, drivelist))
+
+            typebox.setCallback(self.typeboxChange, (typebox, drivelist))        
             self.drivelist = drivelist
 
-            g.addHotKey("F2")
-            screen.pushHelpLine (_("<Space>,<+>,<-> selection   |   <F2> Add drive   |   <F12> next screen"))
+            screen.pushHelpLine (_("<Space>,<+>,<-> selection   |   <F12> next screen"))        
 
             # restore the drive list each time
             disks = pomona.id.diskset.disks.keys()
@@ -984,21 +1565,16 @@ class PartitionTypeWindow:
             rc = g.run()
 
             if len(self.drivelist.getSelection()) > 0:
-                sel = [self.drivelist.getSelection()[0].split()[0]]
+                sel = map(lambda s: s.split()[0], self.drivelist.getSelection())
             else:
                 sel = []
             partmethod_ans = typebox.current()
             res = bb.buttonPressed(rc)
-
+            
             self.clearDrivelist()
             screen.popHelpLine()
             screen.popWindow()
-
-            if rc == "F2":
-                if self.addDriveDialog(screen) != INSTALL_BACK:
-                    partitionObjectsInitialize(pomona)
-                    continue
-
+            
             if res == TEXT_BACK_CHECK:
                 return INSTALL_BACK
 
@@ -1017,21 +1593,18 @@ class PartitionTypeWindow:
 
                 pomona.id.partitions.autoClearPartType = partmethod_ans
                 pomona.id.partitions.autoClearPartDrives = sel
+                break
 
         # ask to review autopartition layout - but only if it's not custom partitioning
         pomona.dispatch.skipStep("partition", skip = 0)
-        pomona.dispatch.skipStep("bootloader", skip = 0)
+        pomona.dispatch.skipStep("bootloader", skip = 1)
 
-        if partmethod_ans != -1:
+       if partmethod_ans != -1:
             reviewLayout = pomona.intf.messageWindow(_("Review Partition Layout"),
-                                                     _("Review and modify partitioning layout?"),
-                                                     type = "yesno")
+                               _("Review and modify partitioning layout?"),
+                               type = "yesno")
 
             if reviewLayout != 1:
                 pomona.dispatch.skipStep("partition", skip = 1)
-                pomona.dispatch.skipStep("bootloader", skip = 1)
 
         return INSTALL_OK
-
-    def addDriveDialog(self, screen):
-        return INSTALL_BACK