]> git.ipfire.org Git - people/ms/ipfire-3.x.git/commitdiff
Added LVM and RAID support to the installer.
authorMichael Tremer <michael.tremer@ipfire.org>
Wed, 18 Feb 2009 10:18:29 +0000 (11:18 +0100)
committerMichael Tremer <michael.tremer@ipfire.org>
Wed, 18 Feb 2009 10:25:50 +0000 (11:25 +0100)
30 files changed:
src/pomona/Makefile
src/pomona/autopart.py
src/pomona/backend.py
src/pomona/bootloader.py
src/pomona/constants.py
src/pomona/flags.py
src/pomona/fsset.py
src/pomona/installer.py
src/pomona/instdata.py
src/pomona/isys/imount.c
src/pomona/isys/imount.h
src/pomona/isys/isys.c
src/pomona/isys/isys.py
src/pomona/iutil.py [moved from src/pomona/inutil.py with 63% similarity]
src/pomona/lvm.py [new file with mode: 0644]
src/pomona/packages.py
src/pomona/pakfireinstall.py
src/pomona/partIntfHelpers.py
src/pomona/partRequests.py
src/pomona/partedUtils.py
src/pomona/partitioning.py
src/pomona/partitions.py
src/pomona/po/pomona.pot
src/pomona/pychecker-false-positives
src/pomona/raid.py [new file with mode: 0644]
src/pomona/tui.py
src/pomona/tui_bootloader.py
src/pomona/tui_keyboard.py
src/pomona/tui_partition.py
src/pomona/tui_timezone.py

index 2a89b233a9c1d1a7203f05487902d64c078c4848..4b549e1a202d71ba74635783e37f380c5df0fa27 100644 (file)
@@ -36,7 +36,7 @@ install: all
                make DESTDIR=`cd $(DESTDIR); pwd` -C $$d install; \
                [ $$? = 0 ] || exit 1; \
        done
-       cp -f *.py lang-{table,names} $(PYTHONLIBDIR)
+       cp -vf *.py lang-{table,names} $(PYTHONLIBDIR)
        chmod 755 $(PYTHONLIBDIR)/installer.py
        sed -e "s/VERSION/$(VERSION)/g" \
                -e "s/SNAME/$(SNAME)/g" \
index 40a1f0f8133a5136af9b81063d051f69d025a47b..67964f9106641541968c25a0ced064fc8afbee3b 100644 (file)
 #
 # autopart.py - auto partitioning logic
 #
-# Jeremy Katz <katzj@redhat.com>
+# Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007  Red Hat, Inc.
+# All rights reserved.
 #
-# Copyright 2001-2005 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 parted
 import copy
 import string, sys
 import fsset
+import lvm
 import logging
-from partitioning import *
+from pomona_log import logger, logFile
+import cryptodev
 import partedUtils
 import partRequests
 from constants import *
-from partErrors import *
+from errors import *
+
+import iutil
+import isys
+
+log = logging.getLogger("pomona")
 
 import gettext
 _ = lambda x: gettext.ldgettext("pomona", x)
-N_ = lambda x: x
 
-log = logging.getLogger("pomona")
 
 PARTITION_FAIL = -1
 PARTITION_SUCCESS = 0
 
-BOOT_ABOVE_1024 = -1
+BOOT_NOT_EXT2 = -1
 BOOTEFI_NOT_VFAT = -2
 BOOTALPHA_NOT_BSD = -3
 BOOTALPHA_NO_RESERVED_SPACE = -4
 BOOTIPSERIES_TOO_HIGH = -5
 
-# check that our "boot" partition meets necessary constraints unless
+DEBUG_LVM_GROW = 0
+
+# Add another logger for the LVM debugging, since there's a lot of that.
+# Set DEBUG_LVM_GROW if you want to spew all this information to the log
+# file.  Otherwise it'll get ignored.
+logger.addLogger ("pomona.lvm", minLevel=logging.DEBUG)
+lvmLog = logging.getLogger("pomona.lvm")
+
+if DEBUG_LVM_GROW:
+    logger.addFileHandler (logFile, lvmLog, minLevel=logging.DEBUG)
+else:
+    lvmLog.setLevel (logging.CRITICAL)
+    logger.addFileHandler (logFile, lvmLog, minLevel=logging.CRITICAL)
+
+# check that our "boot" partitions meet necessary constraints unless
 # the request has its ignore flag set
-def bootRequestCheck(requests, diskset):
-    reqs = requests.getBootableRequest()
-    if not reqs:
+def bootRequestCheck(req, diskset):
+    if not req.device or req.ignoreBootConstraints:
         return PARTITION_SUCCESS
-    for req in reqs:
-        if not req.device or req.ignoreBootConstraints:
-            return PARTITION_SUCCESS
-    # side effect: dev is left as the last in devs
     part = partedUtils.get_partition_by_name(diskset.disks, req.device)
     if not part:
         return PARTITION_SUCCESS
 
-    if partedUtils.end_sector_to_cyl(part.geom.dev, part.geom.end) >= 1024:
-        return BOOT_ABOVE_1024
+    return PARTITION_SUCCESS
+
+# Alpha requires a BSD partition to boot. Since we can be called after:
+#
+#   - We re-attached an existing /boot partition (existing dev.drive)
+#   - We create a new one from a designated disk (no dev.drive)
+#   - We auto-create a new one from a designated set of disks (dev.drive
+#     is a list)
+#
+# it's simpler to get disk the partition belong to through dev.device
+# Some other tests pertaining to a partition where /boot resides are:
+#
+#   - There has to be at least 1 MB free at the begining of the disk
+#     (or so says the aboot manual.)
+
+def bootAlphaCheckRequirements(part):
+    disk = part.disk
+
+    # Disklabel check
+    if not disk.type.name == "bsd":
+        return BOOTALPHA_NOT_BSD
+
+    # The first free space should start at the begining of the drive
+    # and span for a megabyte or more.
+    free = disk.next_partition()
+    while free:
+        if free.type & parted.PARTITION_FREESPACE:
+            break
+        free = disk.next_partition(free)
+    if (not free or free.geom.start != 1L or
+        partedUtils.getPartSizeMB(free) < 1):
+        return BOOTALPHA_NO_RESERVED_SPACE
 
     return PARTITION_SUCCESS
 
+
 def printNewRequestsCyl(diskset, newRequest):
     for req in newRequest.requests:
         if req.type != REQUEST_NEW:
             continue
 
         part = partedUtils.get_partition_by_name(diskset.disks, req.device)
-##      print req
-##      print "Start Cyl:%s    End Cyl: %s" % (partedUtils.start_sector_to_cyl(part.geom.dev, part.geom.start),
-##                                             partedUtils.end_sector_to_cyl(part.geom.dev, part.geom.end))
+##         print(req)
+##         print("Start Cyl:%s    End Cyl: %s" % (partedUtils.start_sector_to_cyl(part.geom.dev, part.geom.start),
+##                                  partedUtils.end_sector_to_cyl(part.geom.dev, part.geom.end)))
 
 def printFreespaceitem(part):
     return partedUtils.get_partition_name(part), part.geom.start, part.geom.end, partedUtils.getPartSizeMB(part)
 
 def printFreespace(free):
-    print "Free Space Summary:"
+    print("Free Space Summary:")
     for drive in free.keys():
-        print "On drive ",drive
+        print("On drive ", drive)
         for part in free[drive]:
-            print "Freespace:", printFreespaceitem(part)
+            print("Freespace:", printFreespaceitem(part))
+
 
 def findFreespace(diskset):
     free = {}
@@ -99,8 +152,8 @@ def bestPartType(disk, request):
     if request.primary:
         return parted.PARTITION_PRIMARY
     if ((numPrimary == (maxPrimary - 1)) and
-                    not disk.extended_partition and
-                    disk.type.check_feature(parted.DISK_TYPE_EXTENDED)):
+        not disk.extended_partition and
+        disk.type.check_feature(parted.DISK_TYPE_EXTENDED)):
         return parted.PARTITION_EXTENDED
     return parted.PARTITION_PRIMARY
 
@@ -126,7 +179,11 @@ class partlist:
 
         self.parts = []
 
-def getMinimumSector():
+def getMinimumSector(disk):
+    if disk.type.name == 'sun':
+        start = long(disk.dev.sectors * disk.dev.heads)
+        start /= long(1024 / disk.dev.sector_size)
+        return start + 1
     return 0L
 
 # first step of partitioning voodoo
@@ -162,18 +219,19 @@ def fitConstrained(diskset, requests, primOnly=0, newParts = None):
                 raise PartitioningError, "Unable to create partition which extends beyond the end of the disk."
 
             # XXX need to check overlaps properly here
-            minSec = getMinimumSector()
+            minSec = getMinimumSector(disk)
             if startSec < minSec:
                 startSec = minSec
 
             if disk.type.check_feature(parted.DISK_TYPE_EXTENDED) and disk.extended_partition:
+
                 if (disk.extended_partition.geom.start < startSec) and (disk.extended_partition.geom.end >= endSec):
                     partType = parted.PARTITION_LOGICAL
                     if request.primary: # they've required a primary and we can't do it
                         raise PartitioningError, "Cannot create another primary partition for %s." % request.mountpoint
                     # check to make sure we can still create more logical parts
                     if (len(partedUtils.get_logical_partitions(disk)) ==
-                            partedUtils.get_max_logical_partitions(disk)):
+                        partedUtils.get_max_logical_partitions(disk)):
                         raise PartitioningError, "Cannot create another logical partition for %s." % request.mountpoint
                 else:
                     partType = parted.PARTITION_PRIMARY
@@ -193,19 +251,18 @@ def fitConstrained(diskset, requests, primOnly=0, newParts = None):
                     partType = parted.PARTITION_LOGICAL
                 else: # shouldn't get here
                     raise PartitioningError, "Impossible partition type to create"
-
             newp = disk.partition_new (partType, fsType, startSec, endSec)
             constraint = disk.dev.constraint_any ()
             try:
                 disk.add_partition (newp, constraint)
 
             except parted.error, msg:
-                raise PartitioningError, msg
+                raise PartitioningError, str(msg)
             for flag in request.fstype.getPartedPartitionFlags():
                 if not newp.is_flag_available(flag):
                     disk.delete_partition(newp)
                     raise PartitioningError, ("requested FileSystemType needs "
-                                                                                                                            "a flag that is not available.")
+                                           "a flag that is not available.")
                 newp.set_flag(flag, 1)
             request.device = fsset.PartedPartitionDevice(newp).getDevice()
             request.currentDrive = request.drive[0]
@@ -247,6 +304,13 @@ 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)
@@ -261,7 +325,7 @@ def fitSized(diskset, requests, primOnly = 0, newParts = None):
 
     for num in number:
         for request in todo[num]:
-#           print "\nInserting ->",request
+#            print("\nInserting ->", request)
             if requests.isBootable(request):
                 isBoot = 1
             else:
@@ -269,14 +333,14 @@ def fitSized(diskset, requests, primOnly = 0, newParts = None):
 
             largestPart = (0, None)
             drives = getDriveList(request, diskset)
-            log.debug("Trying drives to find best free space out of %s" %(free,))
+            lvmLog.debug("Trying drives to find best free space out of %s" %(free,))
             for drive in drives:
                 # this request is bootable and we've found a large enough
                 # partition already, so we don't need to keep trying other
                 # drives.  this keeps us on the first possible drive
                 if isBoot and largestPart[1]:
                     break
-##              print "Trying drive", drive
+##               print("Trying drive", drive)
                 disk = diskset.disks[drive]
                 numPrimary = len(partedUtils.get_primary_partitions(disk))
                 numLogical = len(partedUtils.get_logical_partitions(disk))
@@ -298,113 +362,230 @@ def fitSized(diskset, requests, primOnly = 0, newParts = None):
                         if numLogical == maxLogical:
                             continue
 
-                    log.debug("Trying partition %s" % (printFreespaceitem(part),))
+                    lvmLog.debug( "Trying partition %s" % (printFreespaceitem(part),))
                     partSize = partedUtils.getPartSizeMB(part)
                     # figure out what the request size will be given the
                     # geometry (#130885)
                     requestSectors = long((request.requestSize * 1024L * 1024L) / part.disk.dev.sector_size) - 1
                     requestSizeMB = long((requestSectors * part.disk.dev.sector_size) / 1024L / 1024L)
-                    log.debug("partSize %s  request %s" % (partSize, request.requestSize))
+                    lvmLog.debug("partSize %s  request %s" % (partSize, request.requestSize))
                     if partSize >= requestSizeMB and partSize > largestPart[0]:
                         if not request.primary or (not part.type & parted.PARTITION_LOGICAL):
                             largestPart = (partSize, part)
                             if isBoot:
                                 break
 
-                if not largestPart[1]:
-                    # if the request has a size of zero, it can be allowed to not
-                    # exist without any problems
-                    if request.size > 0:
-                        raise PartitioningError, "Not enough space left to create partition for %s" % request.mountpoint
-                    else:
-                        request.device = None
-                        request.currentDrive = None
-                        continue
-#                                               raise PartitioningError, "Can't fulfill request for partition: \n%s" %(request)
+            if not largestPart[1]:
+                # if the request has a size of zero, it can be allowed to not
+                # exist without any problems
+                if request.size > 0:
+                    raise PartitioningError, "Not enough space left to create partition for %s" % request.mountpoint
+                else:
+                    request.device = None
+                    request.currentDrive = None
+                    continue
+#                raise PartitioningError, "Can't fulfill request for partition: \n%s" %(request)
 
-                log.debug("largestPart is %s" % (largestPart,))
-                freespace = largestPart[1]
-                freeStartSec = freespace.geom.start
-                freeEndSec = freespace.geom.end
+            lvmLog.debug("largestPart is %s" % (largestPart,))
+            freespace = largestPart[1]
+            freeStartSec = freespace.geom.start
+            freeEndSec = freespace.geom.end
 
-                dev = freespace.geom.dev
-                disk = freespace.disk
+            dev = freespace.geom.dev
+            disk = freespace.disk
 
+            startSec = freeStartSec
+
+            endSec = startSec + long(((request.requestSize * 1024L * 1024L) / disk.dev.sector_size)) - 1
+
+            if endSec > freeEndSec:
+                endSec = freeEndSec
+            if startSec < freeStartSec:
                 startSec = freeStartSec
-                endSec = startSec + long(((request.requestSize * 1024L * 1024L) / disk.dev.sector_size)) - 1
 
-                if endSec > freeEndSec:
-                    endSec = freeEndSec
-                if startSec < freeStartSec:
-                    startSec = freeStartSec
+            if freespace.type & parted.PARTITION_LOGICAL:
+                partType = parted.PARTITION_LOGICAL
+            else:
+                # XXX need a better way to do primary vs logical stuff
+                ret = bestPartType(disk, request)
 
-                if freespace.type & parted.PARTITION_LOGICAL:
+                if ret == parted.PARTITION_PRIMARY:
+                    partType = parted.PARTITION_PRIMARY
+                elif ret == parted.PARTITION_EXTENDED:
+                    newp = disk.partition_new(parted.PARTITION_EXTENDED, None, startSec, endSec)
+                    constraint = dev.constraint_any()
+                    disk.add_partition(newp, constraint)
+                    disk.maximize_partition (newp, constraint)
+                    newParts.parts.append(newp)
+                    requests.nextUniqueID = requests.nextUniqueID + 1
                     partType = parted.PARTITION_LOGICAL
-                else:
-                    # XXX need a better way to do primary vs logical stuff
-                    ret = bestPartType(disk, request)
-
-                    if ret == parted.PARTITION_PRIMARY:
-                        partType = parted.PARTITION_PRIMARY
-                    elif ret == parted.PARTITION_EXTENDED:
-                        newp = disk.partition_new(parted.PARTITION_EXTENDED, None, startSec, endSec)
-                        constraint = dev.constraint_any()
-                        disk.add_partition(newp, constraint)
-                        disk.maximize_partition(newp, constraint)
-                        newParts.parts.append(newp)
-                        requests.nextUniqueID = requests.nextUniqueID + 1
-                        partType = parted.PARTITION_LOGICAL
-
-                        # now need to update freespace since adding extended
-                        # took some space
-                        found = 0
-                        part = disk.next_partition()
-                        while part:
-                            if part.type & parted.PARTITION_FREESPACE:
-                                if part.geom.start > freeStartSec and part.geom.end <= freeEndSec:
-                                    found = 1
-                                    freeStartSec = part.geom.start
-                                    freeEndSec = part.geom.end
-                                    break
-
-                            part = disk.next_partition(part)
-
-                        if not found:
-                            raise PartitioningError, "Could not find free space after making new extended partition"
 
+                    # now need to update freespace since adding extended
+                    # took some space
+                    found = 0
+                    part = disk.next_partition()
+                    while part:
+                        if part.type & parted.PARTITION_FREESPACE:
+                            if part.geom.start > freeStartSec and part.geom.end <= freeEndSec:
+                                found = 1
+                                freeStartSec = part.geom.start
+                                freeEndSec = part.geom.end
+                                break
+
+                        part = disk.next_partition(part)
+
+                    if not found:
+                        raise PartitioningError, "Could not find free space after making new extended partition"
+
+                    startSec = freeStartSec
+                    endSec = startSec + long(((request.requestSize * 1024L * 1024L) / disk.dev.sector_size)) - 1
+
+                    if endSec > freeEndSec:
+                        endSec = freeEndSec
+                    if startSec < freeStartSec:
                         startSec = freeStartSec
-                        endSec = startSec + long(((request.requestSize * 1024L * 1024L) / disk.dev.sector_size)) - 1
 
-                        if endSec > freeEndSec:
-                            endSec = freeEndSec
-                            if startSec < freeStartSec:
-                                startSec = freeStartSec
+                else: # shouldn't get here
+                    raise PartitioningError, "Impossible partition to create"
 
-                    else: # shouldn't get here
-                        raise PartitioningError, "Impossible partition to create"
+            fsType = request.fstype.getPartedFileSystemType()
+            lvmLog.debug("creating newp with start=%s, end=%s, len=%s" % (startSec, endSec, endSec - startSec))
+            newp = disk.partition_new (partType, fsType, startSec, endSec)
+            constraint = dev.constraint_any ()
 
-                fsType = request.fstype.getPartedFileSystemType()
-                log.debug("creating newp with start=%s, end=%s, len=%s" % (startSec, endSec, endSec - startSec))
-                newp = disk.partition_new (partType, fsType, startSec, endSec)
-                constraint = dev.constraint_any()
+            try:
+                disk.add_partition (newp, constraint)
+            except parted.error, msg:
+                raise PartitioningError, str(msg)
+            for flag in request.fstype.getPartedPartitionFlags():
+                if not newp.is_flag_available(flag):
+                    disk.delete_partition(newp)
+                    raise PartitioningError, ("requested FileSystemType needs "
+                                           "a flag that is not available.")
+                newp.set_flag(flag, 1)
 
-                try:
-                    disk.add_partition(newp, constraint)
-                except parted.error, msg:
-                    raise PartitioningError, msg
-
-                for flag in request.fstype.getPartedPartitionFlags():
-                    if not newp.is_flag_available(flag):
-                        disk.delete_partition(newp)
-                        raise PartitioningError, ("requested FileSystemType needs "
-                                                                                                                                "a flag that is not available.")
-                    newp.set_flag(flag, 1)
-
-                request.device = fsset.PartedPartitionDevice(newp).getDevice()
-                drive = newp.geom.dev.path[5:]
-                request.currentDrive = drive
-                newParts.parts.append(newp)
-                free = findFreespace(diskset)
+            request.device = fsset.PartedPartitionDevice(newp).getDevice()
+            drive = newp.geom.dev.path[5:]
+            request.currentDrive = drive
+            newParts.parts.append(newp)
+            free = findFreespace(diskset)
+
+# grow logical partitions
+#
+# do this ONLY after all other requests have been allocated
+# we just go through and adjust the size for the logical
+# volumes w/o rerunning process partitions
+#
+def growLogicalVolumes(diskset, requests):
+
+    if requests is None or diskset is None:
+        return
+
+    # iterate over each volume group, grow logical volumes in each
+    for vgreq in requests.requests:
+        if vgreq.type != REQUEST_VG:
+            continue
+
+        lvmLog.info("In growLogicalVolumes, considering VG %s", vgreq)
+        lvreqs = requests.getLVMLVForVG(vgreq)
+
+        if lvreqs is None or len(lvreqs) < 1:
+            lvmLog.info("Apparently it had no logical volume requests, skipping.")
+            continue
+
+        # come up with list of logvol that are growable
+        growreqs = []
+        for lvreq in lvreqs:
+            if lvreq.grow:
+                growreqs.append(lvreq)
+
+        # bail if none defined
+        if len(growreqs) < 1:
+            lvmLog.info("No growable logical volumes defined in VG %s.", vgreq)
+            continue
+
+        lvmLog.info("VG %s has these growable logical volumes: %s",  vgreq.volumeGroupName, reduce(lambda x,y: x + [y.uniqueID], growreqs, []))
+
+        # get remaining free space
+        if DEBUG_LVM_GROW:
+            vgfree = lvm.getVGFreeSpace(vgreq, requests, diskset)
+            lvmLog.debug("Free space in VG after initial partition formation = %s", (vgfree,))
+
+        # store size we are starting at
+        initsize = {}
+        cursize = {}
+        for req in growreqs:
+            size = req.getActualSize(requests, diskset)
+            size = lvm.clampPVSize(size, vgreq.pesize)
+            initsize[req.logicalVolumeName] = size
+            cursize[req.logicalVolumeName] = size
+            if req.maxSizeMB:
+                req.maxSizeMB = lvm.clampPVSize(req.maxSizeMB, vgreq.pesize)
+            lvmLog.debug("init sizes for %s: %s",req.logicalVolumeName, size)
+
+        # now dolly out free space to all growing LVs
+        bailcount = 0
+        while 1:
+            nochange = 1
+            completed = []
+            for req in growreqs:
+                lvmLog.debug("considering %s, start size = %s",req.logicalVolumeName, req.getStartSize())
+
+                # get remaining free space
+                vgfree = lvm.getVGFreeSpace(vgreq, requests, diskset)
+
+                lvmLog.debug("Free space in VG = %s",vgfree)
+
+                # compute fraction of remaining requests this
+                # particular request represents
+                totsize = 0.0
+                for otherreq in growreqs:
+                    if otherreq in completed:
+                        continue
+
+                    lvmLog.debug("adding in %s %s %s", otherreq.logicalVolumeName, otherreq.getStartSize(), otherreq.maxSizeMB)
+
+                    size = otherreq.getActualSize(requests, diskset)
+                    if otherreq.maxSizeMB:
+                        if size < otherreq.maxSizeMB:
+                            totsize = totsize + otherreq.getStartSize()
+                        else:
+                            lvmLog.debug("%s is now at %s, and passed maxsize of %s", otherreq.logicalVolumeName, size, otherreq.maxSizeMB)
+                    else:
+                        totsize = totsize + otherreq.getStartSize()
+
+                lvmLog.debug("totsize -> %s",totsize)
+
+                # if totsize is zero we have no growable reqs left
+                if totsize == 0:
+                    break
+
+                fraction = float(req.getStartSize())/float(totsize)
+
+                newsize = lvm.clampPVSize(vgfree*fraction, vgreq.pesize)
+                newsize += cursize[req.logicalVolumeName]
+
+                if req.maxSizeMB:
+                    newsize = min(newsize, req.maxSizeMB)
+
+                req.size = newsize
+                if req.size != cursize[req.logicalVolumeName]:
+                    nochange = 0
+
+                cursize[req.logicalVolumeName] = req.size
+
+                lvmLog.debug("Name, size, cursize, vgfree, fraction = %s %s %s %s %s", req.logicalVolumeName, req.size, cursize[req.logicalVolumeName], vgfree, fraction)
+
+                completed.append(req)
+
+            if nochange:
+                lvmLog.info("In growLogicalVolumes, no changes in size so breaking")
+                break
+
+            bailcount = bailcount + 1
+            if bailcount > 10:
+                lvmLog.info("In growLogicalVolumes, bailing after 10 interations.")
+                break
 
 # grow partitions
 def growParts(diskset, requests, newParts):
@@ -435,11 +616,11 @@ def growParts(diskset, requests, newParts):
     ####
     newRequest = requests.copy()
 
-##      print "new requests"
-##      printNewRequestsCyl(diskset, requests)
-##      print "orig requests"
-##      printNewRequestsCyl(diskset, newRequest)
-##      print "\n\n\n"
+##     print("new requests")
+##     printNewRequestsCyl(diskset, requests)
+##     print("orig requests")
+##     printNewRequestsCyl(diskset, newRequest)
+##     print("\n\n\n")
 
     (free, freeSize, largestFree) = getFreeSpace(diskset)
 
@@ -461,9 +642,9 @@ def growParts(diskset, requests, newParts):
     if not growable.keys():
         return
 
-##      print "new requests before looping"
-##      printNewRequestsCyl(diskset, requests)
-##      print "\n\n\n"
+##     print("new requests before looping")
+##     printNewRequestsCyl(diskset, requests)
+##     print("\n\n\n")
 
     # loop over all drives, grow all growable partitions one at a time
     grownList = []
@@ -478,17 +659,18 @@ def growParts(diskset, requests, newParts):
         outer_iter = 0
         lastFreeSize = None
         while not donegrowing and outer_iter < 20:
-#                       if less than one sector left, we're done
-#               if drive not in freeSize.keys() or freeSize[drive] == lastFreeSize:
+            # if less than one sector left, we're done
+#            if drive not in freeSize.keys() or freeSize[drive] == lastFreeSize:
             if drive not in freeSize.keys():
-#                               print "leaving outer loop because no more space on %s\n\n" % drive
+#                print("leaving outer loop because no more space on %s\n\n" % drive)
                 break
-##              print "\nAt start:"
-##              print drive,freeSize.keys()
-##              print freeSize[drive], lastFreeSize
-##              print "\n"
+##             print("\nAt start:")
+##             print(drive,freeSize.keys())
+##             print(freeSize[drive], lastFreeSize)
+##             print("\n")
+
+##             print(diskset.diskState())
 
-##              print diskset.diskState()
 
             outer_iter = outer_iter + 1
             donegrowing = 1
@@ -527,11 +709,11 @@ def growParts(diskset, requests, newParts):
 
                 if drive not in freeSize.keys():
                     donegrowing = 1
-#                                       print "leaving inner loop because no more space on %s\n\n" % drive
+#                    print("leaving inner loop because no more space on %s\n\n" % drive)
                     break
 
-##              print "\nprocessing ID",request.uniqueID, request.mountpoint
-##              print "growSize, freeSize = ",growSize[drive], freeSize[drive]
+##                 print("\nprocessing ID",request.uniqueID, request.mountpoint)
+##                 print("growSize, freeSize = ",growSize[drive], freeSize[drive])
 
                 donegrowing = 0
 
@@ -544,12 +726,12 @@ def growParts(diskset, requests, newParts):
                 percent = origSize[request.uniqueID] / (growSize[drive] * 1.0)
                 growby = long(percent * thisFreeSize[drive])
                 if growby < cylsectors:
-                    growby = cylsectors
+                    growby = cylsectors;
                 maxsect = startSize + growby
 
-##                      print request
-##                      print "percent, growby, maxsect, free", percent, growby, maxsect,freeSize[drive], startSize, lastFreeSize
-##                      print "max is ",maxsect
+##                 print(request)
+##                 print("percent, growby, maxsect, free", percent, growby, maxsect,freeSize[drive], startSize, lastFreeSize)
+##                 print("max is ", maxsect)
 
                 imposedMax = 0
                 if request.maxSizeMB:
@@ -560,11 +742,12 @@ def growParts(diskset, requests, newParts):
                     if maxsect > maxUserSize:
                         maxsect = long(maxUserSize)
                         imposedMax = 1
+
                 else:
                     # XXX HACK enforce silent limit for swap otherwise it
                     #     can grow up to 2TB!
                     if request.fstype.name == "swap":
-                        (xxxint, tmpint) = inutil.swapSuggestion(quiet=1)
+                        (xxxint, tmpint) = iutil.swapSuggestion(quiet=1)
 
                         # convert to sectors
                         tmpint = tmpint*1024*1024/sector_size
@@ -574,106 +757,139 @@ def growParts(diskset, requests, newParts):
                         if maxsugswap >= userstartsize:
                             maxsect = maxsugswap
                             imposedMax = 1
-                            log.warning("Enforced max swap size of %s based on suggested max swap", maxsect)
-
-                    # round max fs limit down a cylinder, helps when growing
-                    # so we don't end up with a free cylinder at end if
-                    # maxlimit fell between cylinder boundaries
-                    tmpint = request.fstype.getMaxSizeMB()*1024.0*1024.0/sector_size
-                    tmpint = long(tmpint / cylsectors)
-                    maxFSSize = tmpint * cylsectors
-                    if maxsect > maxFSSize:
-                        maxsect = long(maxFSSize)
-                        imposedMax = 1
+                            lvmLog.warning("Enforced max swap size of %s based on suggested max swap", maxsect)
+
+
+                # round max fs limit down a cylinder, helps when growing
+                # so we don't end up with a free cylinder at end if
+                # maxlimit fell between cylinder boundaries
+                tmpint = request.fstype.getMaxSizeMB()*1024.0*1024.0/sector_size
+                tmpint = long(tmpint / cylsectors)
+                maxFSSize = tmpint * cylsectors
+                if maxsect > maxFSSize:
+                    maxsect = long(maxFSSize)
+                    imposedMax = 1
+
+                maxfree = largestFree[drive]
+                if maxsect > maxfree + startSize:
+                    maxsect = long(maxfree) + startSize
+                    imposedMax = 1
+
+#                print("freesize, max, maxfree = ",freeSize[drive],maxsect, maxfree)
+#                print("freeSizeMB, maxMB = ", freeSize[drive] * sector_size/(1024.0 * 1024.0), maxsect * sector_size/(1024.0*1024.0), largestFree[drive] * sector_size/(1024.0*1024.0))
+#                print("startsize = ", startSize)
+
+                min = startSize
+                max = maxsect
+                diff = max - min
+                cur = max - (diff / 2)
+                lastDiff = 0
+
+                # binary search
+##                 print("start min, max, cur, diffs = ",min,max,cur,diff,lastDiff)
+                inner_iter = 0
+                ret = PARTITION_SUCCESS # request succeeded with initial size
+                while (max != min) and (lastDiff != diff) and (inner_iter < 2000):
+##                     printNewRequestsCyl(diskset, newRequest)
+
+                    # XXX need to request in sectors preferably, more accurate
+##                  print("trying cur=%s" % cur)
+                    request.requestSize = (cur*sector_size)/1024.0/1024.0
+
+                    # try adding
+                    try:
+                        processPartitioning(diskset, newRequest, newParts)
+                        min = cur
+                    except PartitioningError, msg:
+                        ret = PARTITION_FAIL
+                        max = cur
+##                        print("!!!!!!!!!!! processPartitioning failed - %s" % msg)
 
-                    maxfree = largestFree[drive]
-                    if maxsect > largestFree[drive]:
-                        maxsect = long(maxfree) + startSize
-                        imposedMax = 1
+                    lastDiff = diff
+                    diff = max - min
 
-#                   print "freesize, max, maxfree = ",freeSize[drive],maxsect, maxfree
-#                   print "freeSizeMB, maxMB = ", freeSize[drive] * sector_size/(1024.0 * 1024.0), maxsect * sector_size/(1024.0*1024.0), largestFree[drive] * sector_size/(1024.0*1024.0)
-#                   print "startsize = ",startSize
+#                    print(min, max, diff, cylsectors)
+#                    print(diskset.diskState())
 
-                    min = startSize
-                    max = maxsect
-                    diff = max - min
                     cur = max - (diff / 2)
-                    lastDiff = 0
-
-#                   binary search
-##                  print "start min, max, cur, diffs = ",min,max,cur,diff,lastDiff
-                    inner_iter = 0
-                    ret = PARTITION_SUCCESS # request succeeded with initial size
-                    while (max != min) and (lastDiff != diff) and (inner_iter < 2000):
-##                      printNewRequestsCyl(diskset, newRequest)
-
-                        # XXX need to request in sectors preferably, more accurate
-##                      print "trying cur=%s" % cur
-                        request.requestSize = (cur*sector_size)/1024.0/1024.0
-
-                        # try adding
-                        try:
-                            processPartitioning(diskset, newRequest, newParts)
-                            min = cur
-                        except PartitioningError, msg:
-                            ret = PARTITION_FAIL
-                            max = cur
-##                          print "!!!!!!!!!!! processPartitioning failed - %s" % msg
-
-                        lastDiff = diff
-                        diff = max - min
-
-#                       print min, max, diff, cylsectors
-#                       print diskset.diskState()
-
-                        cur = max - (diff / 2)
-
-                        inner_iter = inner_iter + 1
-#                       print "sizes at end of loop - cur: %s min:%s max:%s diff:%s lastDiff:%s" % (cur,min,max,diff,lastDiff)
-
-#                       freeSize[drive] = freeSize[drive] - (min - startSize)
-#                       print "shrinking freeSize to ",freeSize[drive], lastFreeSize
-#                       if freeSize[drive] < 0:
-#                               print "freesize < 0!"
-#                               freeSize[drive] = 0
-
-                    # we could have failed on the last try, in which case we
-                    # should go back to the smaller size
-                    if ret == PARTITION_FAIL:
-#                       print "growing finally failed at size", min
-                        request.requestSize = min*sector_size/1024.0/1024.0
-                        processPartitioning(diskset, newRequest, newParts)
 
-#                   print "end min, max, cur, diffs = ",min,max,cur,diff,lastDiff
-#                   print "%s took %s loops" % (request.mountpoint, inner_iter)
-                    lastFreeSize = freeSize[drive]
-                    (free, freeSize, largestFree) = getFreeSpace(diskset)
-#                   print Freespace(free)
-
-                    if ret == PARTITION_FAIL or (max == maxsect and imposedMax):
-#                       print "putting ",request.uniqueID,request.mountpoint," in grownList"
-                        grownList.append(request.uniqueID)
-                        growSize[drive] = growSize[drive] - origSize[request.uniqueID]
-                        if growSize[drive] < 0:
-#                           print "growsize < 0!"
-                            growSize[drive] = 0
+                    inner_iter = inner_iter + 1
+#                    print("sizes at end of loop - cur: %s min:%s max:%s diff:%s lastDiff:%s" % (cur,min,max,diff,lastDiff))
+
+#                freeSize[drive] = freeSize[drive] - (min - startSize)
+#                print("shrinking freeSize to ",freeSize[drive], lastFreeSize)
+#                if freeSize[drive] < 0:
+#                    print("freesize < 0!")
+#                    freeSize[drive] = 0
+
+                # we could have failed on the last try, in which case we
+                # should go back to the smaller size
+                if ret == PARTITION_FAIL:
+#                    print("growing finally failed at size", min)
+                    request.requestSize = min*sector_size/1024.0/1024.0
+                    processPartitioning(diskset, newRequest, newParts)
+
+#                print("end min, max, cur, diffs = ",min,max,cur,diff,lastDiff)
+#                print("%s took %s loops" % (request.mountpoint, inner_iter))
+                lastFreeSize = freeSize[drive]
+                (free, freeSize, largestFree) = getFreeSpace(diskset)
+#                print(Freespace(free))
+
+                if ret == PARTITION_FAIL or (max == maxsect and imposedMax):
+#                    print("putting ",request.uniqueID,request.mountpoint," in grownList")
+                    grownList.append(request.uniqueID)
+                    growSize[drive] = growSize[drive] - origSize[request.uniqueID]
+                    if growSize[drive] < 0:
+#                        print("growsize < 0!")
+                        growSize[drive] = 0
 
 def setPreexistParts(diskset, requests):
     for request in requests:
         if request.type != REQUEST_PREEXIST:
             continue
         if not diskset.disks.has_key(request.drive):
-            log.info("pre-existing partition on non-native disk %s, ignoring" %(request.drive,))
+            lvmLog.info("pre-existing partition on non-native disk %s, ignoring" %(request.drive,))
             continue
         disk = diskset.disks[request.drive]
         part = disk.next_partition()
         while part:
             if part.geom.start == request.start and part.geom.end == request.end:
+                if partedUtils.isEfiSystemPartition(part) and \
+                        request.fstype.name == "vfat":
+                    request.fstype = fsset.fileSystemTypeGet("efi")
+                # if the partition is being resized, we do that now
+                if request.targetSize is not None:
+                    startSec = part.geom.start
+                    endSec = part.geom.start + long(((request.targetSize * 1024L * 1024L) / disk.dev.sector_size)) - 1
+
+                    try:
+                        g = part.geom.duplicate()
+                        g.set_end(endSec)
+                        constraint = g.constraint_exact()
+                        part.set_geometry(constraint, startSec, endSec)
+                    except parted.error, msg:
+                        log.error("error setting geometry for partition %s: %s" %(partedUtils.get_partition_name(part), msg))
+                        raise PartitioningError, _("Error resizing partition %s.\n\n%s") %(partedUtils.get_partition_name(part), msg)
+
+                    if startSec != part.geom.start:
+                        raise PartitioningError, _("Start of partition %s was moved when resizing") %(partedUtils.get_partition_name(part),)
+
                 request.device = partedUtils.get_partition_name(part)
                 if request.fstype:
                     if request.fstype.getName() != request.origfstype.getName():
+                        if part.is_flag_available(parted.PARTITION_RAID):
+                            if request.fstype.getName() == "software RAID":
+                                part.set_flag(parted.PARTITION_RAID, 1)
+                            else:
+                                part.set_flag(parted.PARTITION_RAID, 0)
+                        if part.is_flag_available(parted.PARTITION_LVM):
+                            if request.fstype.getName() == "physical volume (LVM)":
+                                part.set_flag(parted.PARTITION_LVM, 1)
+                            else:
+                                part.set_flag(parted.PARTITION_LVM, 0)
+
                         partedUtils.set_partition_file_system_type(part, request.fstype)
+
                 break
             part = disk.next_partition(part)
 
@@ -703,7 +919,7 @@ def processPartitioning(diskset, requests, newParts):
     dellist = []
     for part in newParts.parts:
         if (part.type & parted.PARTITION_LOGICAL
-                        and extendeds.has_key(part.geom.dev.path)):
+            and extendeds.has_key(part.geom.dev.path)):
             dellist.append(part)
 
     for part in dellist:
@@ -748,27 +964,53 @@ def processPartitioning(diskset, requests, newParts):
     try:
         fitConstrained(diskset, requests, 1, newParts)
     except PartitioningError, msg:
-        raise PartitioningError, _("Could not allocate cylinder-based partitions as primary partitions.\n\n%s") % msg
+        raise PartitioningError, _("Could not allocate cylinder-based partitions as primary partitions.\n") + str(msg)
 
     try:
         fitSized(diskset, requests, 1, newParts)
     except PartitioningError, msg:
-        raise PartitioningError, _("Could not allocate partitions as primary partitions.\n\n%s") % msg
+        raise PartitioningError, _("Could not allocate partitions as primary partitions.\n") + str(msg)
 
     try:
         fitConstrained(diskset, requests, 0, newParts)
     except PartitioningError, msg:
-        raise PartitioningError, _("Could not allocate cylinder-based partitions.\n\n%s") % msg
+        raise PartitioningError, _("Could not allocate cylinder-based partitions.\n") + str(msg)
 
     # Don't need to handle the exception here since we leave the message alone.
     fitSized(diskset, requests, 0, newParts)
 
     for request in requests.requests:
+        # set the unique identifier for raid and lvm devices
+        if request.type == REQUEST_RAID and not request.device:
+            request.device = str(request.uniqueID)
+        if request.type == REQUEST_VG and not request.device:
+            request.device = str(request.uniqueID)
+        # anything better we can use for the logical volume?
+        if request.type == REQUEST_LV and not request.device:
+            request.device = str(request.uniqueID)
+
         if not request.device:
             raise PartitioningError, "Unsatisfied partition request\n%s" % request
 
-##      print "disk layout after everything is done"
-##      print diskset.diskState()
+    # get the sizes for raid devices, vgs, and logical volumes
+    for request in requests.requests:
+        if request.type == REQUEST_RAID:
+            request.size = request.getActualSize(requests, diskset)
+        elif request.type == REQUEST_VG:
+            request.size = request.getActualSize(requests, diskset)
+        elif request.type == REQUEST_LV:
+            if request.grow:
+                request.setSize(request.getStartSize())
+            else:
+                request.size = request.getActualSize(requests, diskset)
+        elif request.preexist:
+            # we need to keep track of the max size of preexisting partitions
+            # FIXME: we should also get the max size for LVs at some point
+            part = partedUtils.get_partition_by_name(diskset.disks, request.device)
+            request.maxResizeSize = partedUtils.getMaxAvailPartSizeMB(part)
+
+##     print("disk layout after everything is done")
+##     print(diskset.diskState())
 
 def doPartitioning(diskset, requests, doRefresh = 1):
     for request in requests.requests:
@@ -794,24 +1036,49 @@ def doPartitioning(diskset, requests, doRefresh = 1):
 
     newParts.reset()
 
-    ret = bootRequestCheck(requests, diskset)
-
-    if ret == BOOTALPHA_NOT_BSD:
-        raise PartitioningWarning, _("Boot partition %s doesn't belong to a BSD disk label. SRM won't be able to boot from this partition. Use a partition belonging to a BSD disk label or change this device disk label to BSD.") %(requests.getBootableRequest()[0].mountpoint,)
-    elif ret == BOOTALPHA_NO_RESERVED_SPACE:
-        raise PartitioningWarning, _("Boot partition %s doesn't belong to a disk with enough free space at its beginning for the bootloader to live on. Make sure that there's at least 5MB of free space at the beginning of the disk that contains /boot") %(requests.getBootableRequest()[0].mountpoint,)
-    elif ret == BOOTEFI_NOT_VFAT:
-        raise PartitioningError, _("Boot partition %s isn't a VFAT partition.  EFI won't be able to boot from this partition.") %(requests.getBootableRequest()[0].mountpoint,)
-    elif ret == BOOTIPSERIES_TOO_HIGH:
-        raise PartitioningError, _("The boot partition must entirely be in the first 4GB of the disk.  OpenFirmware won't be able to boot this installation.")
-    elif ret == BOOT_ABOVE_1024:
-        # we can't make boot disks anymore and this isn't much of a problem
-        # for "modern" hardware. (#122535)
-        pass
-    elif ret != PARTITION_SUCCESS:
-        # more specific message?
-        raise PartitioningWarning, _("Boot partition %s may not meet booting constraints for your architecture.") %(requests.getBootableRequest()[0].mountpoint,)
-#       raise PartitioningWarning, _("Boot partition %s may not meet booting constraints for your architecture.  Creation of a boot disk is highly encouraged.") %(requests.getBootableRequest()[0].mountpoint,)
+    for req in requests.getBootableRequest() or []:
+        ret = bootRequestCheck(req, diskset)
+        if ret == BOOTALPHA_NOT_BSD:
+            raise PartitioningWarning, _("Boot partition %s doesn't belong to a BSD disk label. SRM won't be able to boot from this partition. Use a partition belonging to a BSD disk label or change this device disk label to BSD.") %(req.mountpoint,)
+        elif ret == BOOTALPHA_NO_RESERVED_SPACE:
+            raise PartitioningWarning, _("Boot partition %s doesn't belong to a disk with enough free space at its beginning for the bootloader to live on. Make sure that there's at least 5MB of free space at the beginning of the disk that contains /boot") %(req.mountpoint,)
+        elif ret == BOOTEFI_NOT_VFAT:
+            raise PartitioningError, _("Boot partition %s isn't a VFAT partition.  EFI won't be able to boot from this partition.") %(req.mountpoint,)
+        elif ret == BOOTIPSERIES_TOO_HIGH:
+            raise PartitioningError, _("The boot partition must entirely be in the first 4GB of the disk.  OpenFirmware won't be able to boot this installation.")
+        elif req == BOOT_NOT_EXT2:
+            raise PartitioningError, _("Boot partition %s is not a Linux filesystem, such as ext3.  The system won't be able to boot from this partition.") %(req.mountpoint,)
+        elif ret != PARTITION_SUCCESS:
+            # more specific message?
+            raise PartitioningWarning, _("Boot partition %s may not meet booting constraints for your architecture.") %(req.mountpoint,)
+
+    # now grow the logical partitions
+    growLogicalVolumes(diskset, requests)
+
+    # make sure our logical volumes still fit
+    #
+    # XXXX should make all this used lvm.getVGFreeSpace() and
+    # lvm.getVGUsedSpace() at some point
+    #
+
+    vgused = {}
+    for request in requests.requests:
+        if request.type == REQUEST_LV:
+            size = int(request.getActualSize(requests, diskset, True))
+            if vgused.has_key(request.volumeGroup):
+                vgused[request.volumeGroup] = (vgused[request.volumeGroup] +
+                                               size)
+            else:
+                vgused[request.volumeGroup] = size
+
+    for vg in vgused.keys():
+        request = requests.getRequestByID(vg)
+        lvmLog.info("Used size vs. available for vg %s:  %s %s", request.volumeGroupName, vgused[vg], request.getActualSize(requests, diskset))
+        if vgused[vg] > request.getActualSize(requests, diskset):
+            raise PartitioningError, _("Adding this partition would not "
+                                       "leave enough disk space for already "
+                                       "allocated logical volumes in "
+                                       "%s." % (request.volumeGroupName))
 
 # given clearpart specification execute it
 # probably want to reset diskset and partition request lists before calling
@@ -823,6 +1090,8 @@ def doClearPartAction(pomona, partitions, diskset):
 
     if type == CLEARPART_TYPE_ALL:
         linuxOnly = 0
+    elif type == CLEARPART_TYPE_NONE:
+        return
     else:
         raise ValueError, "Invalid clear part type in doClearPartAction"
 
@@ -853,16 +1122,20 @@ def doClearPartAction(pomona, partitions, diskset):
             #    a RAID or LVM device (#107319)
             # 5) the drive contains protected partitions and initAll is set
             if ((linuxOnly == 0) or (ptype and ptype.isLinuxNativeFS()) or
+                (initAll and
+                 partedUtils.hasProtectedPartitions(drive, pomona)) or
                 (not ptype and
                  partedUtils.isLinuxNativeByNumtype(part.native_type)) or
                 ((part.native_type == -1) and # the ptable doesn't have types
                  ((part.is_flag_available(parted.PARTITION_RAID) and part.get_flag(parted.PARTITION_RAID)) or  # this is a RAID
-                  (part.is_flag_available(parted.PARTITION_LVM) and part.get_flag(parted.PARTITION_LVM))))): # or an LVM
+                  (part.is_flag_available(parted.PARTITION_LVM) and part.get_flag(parted.PARTITION_LVM)) # or an LVM
+                  ))):
                 old = partitions.getRequestByDeviceName(partedUtils.get_partition_name(part))
                 if old.getProtected():
                     part = disk.next_partition(part)
                     continue
 
+                partitions.deleteDependentRequests(old)
                 partitions.removeRequest(old)
 
                 drive = partedUtils.get_partition_drive(part)
@@ -896,12 +1169,17 @@ 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)
+        partitions.setProtected(pomona.dispatch)
         partitions.autoPartitionRequests = []
         return
 
@@ -911,7 +1189,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
-        setDefaultPartitioning(partitions, doClear = 0)
+        instClass.setDefaultPartitioning(partitions, doClear = 0)
 
     # reset drive and request info to original state
     # XXX only do this if we're dirty
@@ -922,8 +1200,10 @@ def doAutoPartition(pomona):
     # XXX clearpartdrives is overloaded as drives we want to use for linux
     drives = []
     initial_free = findFreespace(diskset)
+    initial_free_keys = initial_free.keys()
+
     if partitions.autoClearPartDrives:
-        for drive in partitions.autoClearPartDrives:
+        for drive in filter (lambda d: d in initial_free_keys, partitions.autoClearPartDrives):
             free = 0
             for f in initial_free[drive]:
                 size = f.geom.end - f.geom.start
@@ -951,7 +1231,7 @@ def doAutoPartition(pomona):
                 pomona.intf.messageWindow(_("Requested Partition Does Not Exist"),
                                    _("Unable to locate partition %s to use "
                                      "for %s.\n\n"
-                                     "Press 'OK' to reboot your system.")
+                                     "Press 'OK' to exit the installer.")
                                    % (request.device, request.mountpoint),
                                    custom_icon='error')
                 sys.exit(0)
@@ -960,8 +1240,103 @@ def doAutoPartition(pomona):
             # preexisting partition's request... ladeda
             if request.mountpoint:
                 req.mountpoint = request.mountpoint
-            if request.badblocks:
-                req.badblocks = request.badblocks
+            if request.uniqueID:  # for raid to work
+                req.uniqueID = request.uniqueID
+            if request.fsopts:
+                req.fsopts = request.fsopts
+            if not request.format:
+                req.format = 0
+            else:
+                req.format = 1
+                req.fstype = request.fstype
+        # XXX whee!  lots of cut and paste code lies below
+        elif (isinstance(request, partRequests.RaidRequestSpec) and
+              request.preexist == 1):
+            req = partitions.getRequestByDeviceName(request.device)
+            if not req or req.preexist == 0:
+                pomona.intf.messageWindow(_("Requested Raid Device Does Not Exist"),
+                                   _("Unable to locate raid device %s to use "
+                                     "for %s.\n\n"
+                                     "Press 'OK' to exit the installer.")
+                                   % (request.device,
+                                      request.mountpoint),
+                                   custom_icon='error')
+                sys.exit(0)
+
+            # now go through and set things from the request to the
+            # preexisting partition's request... ladeda
+            if request.mountpoint:
+                req.mountpoint = request.mountpoint
+            if request.uniqueID:  # for raid to work
+                req.uniqueID = request.uniqueID
+            if request.fsopts:
+                req.fsopts = request.fsopts
+            if not request.format:
+                req.format = 0
+            else:
+                req.format = 1
+                req.fstype = request.fstype
+            # XXX not copying the raid bits because they should be handled
+            # automagically (actually, people probably aren't specifying them)
+
+        elif (isinstance(request, partRequests.VolumeGroupRequestSpec) and
+              request.preexist == 1):
+            # get the preexisting partition they want to use
+            req = partitions.getRequestByVolumeGroupName(request.volumeGroupName)
+            if not req or req.preexist == 0 or req.format == 1:
+                pomona.intf.messageWindow(_("Requested Volume Group Does Not Exist"),
+                                   _("Unable to locate volume group %s to use "
+                                     "for %s.\n\n"
+                                     "Press 'OK' to exit the installer.")
+                                  % (request.volumeGroupName,
+                                     request.mountpoint),
+                                   custom_icon='error')
+                sys.exit(0)
+
+            oldid = None
+            # now go through and set things from the request to the
+            # preexisting partition's request... ladeda
+            if request.physicalVolumes:
+                req.physicalVolumes = request.physicalVolumes
+            if request.pesize:
+                req.pesize = request.pesize
+            if request.uniqueID:  # for raid to work
+                oldid = req.uniqueID
+                req.uniqueID = request.uniqueID
+            if request.fsopts:
+                req.fsopts = request.fsopts
+            if not request.format:
+                req.format = 0
+            else:
+                req.format = 1
+
+            # we also need to go through and remap everything which we
+            # previously found to our new id.  yay!
+            if oldid is not None:
+                for lv in partitions.getLVMLVForVGID(oldid):
+                    lv.volumeGroup = req.uniqueID
+
+
+        elif (isinstance(request, partRequests.LogicalVolumeRequestSpec) and
+              request.preexist == 1):
+            # get the preexisting partition they want to use
+            req = partitions.getRequestByLogicalVolumeName(request.logicalVolumeName)
+            if not req or req.preexist == 0:
+                pomona.intf.messageWindow(_("Requested Logical Volume Does Not Exist"),
+                                   _("Unable to locate logical volume %s to use "
+                                     "for %s.\n\n"
+                                     "Press 'OK' to exit the installer.")
+                                   % (request.logicalVolumeName,
+                                      request.mountpoint),
+                                   custom_icon='error')
+                sys.exit(0)
+
+            # now go through and set things from the request to the
+            # preexisting partition's request... ladeda
+            if request.volumeGroup:
+                req.volumeGroup = request.volumeGroup
+            if request.mountpoint:
+                req.mountpoint = request.mountpoint
             if request.uniqueID:  # for raid to work
                 req.uniqueID = request.uniqueID
             if request.fsopts:
@@ -976,6 +1351,13 @@ def doAutoPartition(pomona):
 
             if req.type == REQUEST_NEW and not req.drive:
                 req.drive = drives
+
+            # this is kind of a hack, but if we're doing autopart encryption
+            # and the request is a PV, encrypt it
+            if partitions.autoEncrypt and req.type == REQUEST_NEW and \
+               isinstance(req.fstype, fsset.lvmPhysicalVolumeDummyFileSystem):
+                req.encryption = cryptodev.LUKSDevice(passphrase=partitions.encryptionPassphrase, format=1)
+
             # if this is a multidrive request, we need to create one per drive
             if req.type == REQUEST_NEW and req.multidrive:
                 if not req.drive:
@@ -983,48 +1365,144 @@ def doAutoPartition(pomona):
 
                 for drive in req.drive:
                     r = copy.copy(req)
+                    r.encryption = copy.deepcopy(req.encryption)
                     r.drive = [ drive ]
                     partitions.addRequest(r)
                 continue
 
+            if (isinstance(req, partRequests.VolumeGroupRequestSpec)):
+                # if the number of physical volumes requested is zero, then
+                # add _all_ physical volumes we can find
+                if ((len(req.physicalVolumes) == 0)
+                    or (not req.physicalVolumes)):
+                    req.physicalVolumes = []
+                    for r in partitions.requests:
+                        if isinstance(r.fstype,
+                                      fsset.lvmPhysicalVolumeDummyFileSystem):
+                            valid = 0
+                            if ((not partitions.autoClearPartDrives) or
+                                len(partitions.autoClearPartDrives) == 0):
+                                valid = 1
+                            else:
+                                if not isinstance(r, partRequests.RaidRequestSpec):
+                                    for d in r.drive:
+                                        if d in partitions.autoClearPartDrives:
+                                            valid = 1
+                                            break
+
+                            if not isinstance(r, partRequests.RaidRequestSpec):
+                                if not r.multidrive:
+                                    valid = 0
+
+                            if valid:
+                                req.physicalVolumes.append(r.uniqueID)
+                    # FIXME: this is a hack so that autopartition'd vgs
+                    # can have a unique name
+                    if req.autoname == 1 and req.volumeGroupName == "lvm":
+                        n = lvm.createSuggestedVGName(partitions, pomona.id.network)
+                        req.volumeGroupName = n
+
+            if (isinstance(req, partRequests.LogicalVolumeRequestSpec)):
+                # if the volgroup is set to a string, we probably need
+                # to find that volgroup and use it's id
+                if type(req.volumeGroup) == type(""):
+                    r = None
+                    if req.volumeGroup == "lvm":
+                        for p in partitions.requests:
+                            if isinstance(p, partRequests.VolumeGroupRequestSpec) and p.autoname == 1:
+                                r = p
+                                break
+                    else:
+                        r = partitions.getRequestByVolumeGroupName(req.volumeGroup)
+                    if r is not None:
+                        req.volumeGroup = r.uniqueID
+                    else:
+                        raise RuntimeError, "Unable to find the volume group for logical volume %s" %(req.logicalVolumeName,)
+
             partitions.addRequest(req)
 
+    # Remove all preexisting VG requests that reference nonexistant PV
+    # requests.  These VGs should only be present on installs where we're
+    # using preexisting partitions that already have LVM information.  We
+    # need to do the same thing for preexisting RAID requests, as well.
+    removeReqs = []
+
+    for req in partitions.requests:
+        if isinstance(req, partRequests.VolumeGroupRequestSpec):
+            lst = req.physicalVolumes
+        elif isinstance(req, partRequests.RaidRequestSpec):
+            lst = req.raidmembers
+        else:
+            continue
+
+        if len(filter (lambda id: partitions.getRequestByID(id) != None, lst)) == 0:
+            removeReqs.append(req)
+
+    for req in removeReqs:
+        partitions.removeRequest(req)
+
+    removeReqs = []
+
+    # Now that we've removed bad VGs, remove all LVs that would have
+    # resided on those VGs.
+    for req in filter (lambda r: isinstance(r, partRequests.LogicalVolumeRequestSpec), partitions.requests):
+        if partitions.getRequestByID(req.volumeGroup) == None:
+            removeReqs.append(req)
+
+    for req in removeReqs:
+        partitions.removeRequest(req)
+
     # sanity checks for the auto partitioning requests; mostly only useful
     # for kickstart as our installclass defaults SHOULD be sane
     for req in partitions.requests:
         errors = req.sanityCheckRequest(partitions)
         if errors:
             pomona.intf.messageWindow(_("Automatic Partitioning Errors"),
-                                      _("The following errors occurred with your "
-                                        "partitioning:\n\n%s\n\n"
-                                        "Press 'OK' to reboot your system.")
-                                      % (errors,), custom_icon='error')
+                               _("The following errors occurred with your "
+                                 "partitioning:\n\n%s\n\n"
+                                 "Press 'OK' to exit the installer.") %
+                               (errors,), custom_icon='error')
             sys.exit(0)
 
     try:
         doPartitioning(diskset, partitions, doRefresh = 0)
     except PartitioningWarning, msg:
-        pomona.intf.messageWindow(_("Warnings During Automatic Partitioning"),
-                                  _("Following warnings occurred during automatic "
-                                    "partitioning:\n\n%s") % (msg.value,),
-                                  custom_icon='warning')
+        if not pomona.isKickstart:
+            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)
-        pomona.dispatch.skipStep("partition", skip = 0)
+        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.intf.messageWindow(_("Error Partitioning"),
                _("Could not allocate requested partitions: \n\n"
-                 "%s.") % (msg.value), custom_icon='error')
+                 "%s.%s") % (msg, extra), custom_icon='error')
+
+
+        if pomona.isKickstart:
+            sys.exit(0)
 
     # now do a full check of the requests
     (errors, warnings) = partitions.sanityCheckAllRequests(diskset)
     if warnings:
         for warning in warnings:
-            log.warning(warning)
+            lvmLog.warning(warning)
     if errors:
         errortxt = string.join(errors, '\n')
-        extra = _("\n\nPress 'OK' to choose a different partitioning option.")
+        if pomona.isKickstart:
+            extra = _("\n\nPress 'OK' to exit the installer.")
+        else:
+            extra = _("\n\nPress 'OK' to choose a different partitioning option.")
 
         pomona.intf.messageWindow(_("Automatic Partitioning Errors"),
                            _("The following errors occurred with your "
@@ -1034,6 +1512,13 @@ 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):
@@ -1068,71 +1553,83 @@ def autoCreatePartitionRequests(autoreq):
 
     return requests
 
-def getAutopartitionBoot():
-    """Return the proper shorthand for the boot dir"""
-    return [ ("/boot", None, 100, None, 0, 1, 0) ]
-
-def queryAutoPartitionOK(pomona):
-    type = pomona.id.partitions.autoClearPartType
-    drives = pomona.id.partitions.autoClearPartDrives
+def autoCreateLVMPartitionRequests(autoreq):
+    """Return a list of requests created with a shorthand notation using LVM.
 
-    if type == CLEARPART_TYPE_ALL:
-        msg = CLEARPART_TYPE_ALL_WARNING_MSG
-    else:
-        raise ValueError, "Invalid clear part type in queryAutoPartitionOK"
-
-    drvstr = "\n\n"
-    if drives == None:
-        drives = pomona.id.diskset.disks.keys()
+    Mainly used by installclasses; make a list of tuples of the form
+    (mntpt, fstype, minsize, maxsize, grow, format)
+    mntpt = None for non-mountable, otherwise is mount point
+    fstype = None to use default, otherwise a string
+    minsize = smallest size
+    maxsize = max size, or None means no max
+    grow = 0 or 1, should partition be grown
+    format = 0 or 1, whether to format
+    asvol = 0 or 1, whether or not it should be a logical volume
+    """
 
-    drives.sort()
-    width = 44
-    str = ""
-    maxlen = 0
-    for drive in drives:
-        if (len(drive) > maxlen):
-            maxlen = len(drive)
-            maxlen = maxlen + 8   # 5 for /dev/, 3 for spaces
-    for drive in drives:
-        if (len(str) + maxlen <= width):
-            str = str + "%-*s" % (maxlen, "/dev/"+drive)
+    requests = []
+    nr = partRequests.PartitionSpec(fsset.fileSystemTypeGet("physical volume (LVM)"),
+                                    mountpoint = None,
+                                    size = 0,
+                                    maxSizeMB = None,
+                                    grow = 1,
+                                    format = 1,
+                                    multidrive = 1)
+
+    requests.append(nr)
+    nr = partRequests.VolumeGroupRequestSpec(fstype = None,
+                                             vgname = "lvm",
+                                             physvols = [],
+                                             format = 1)
+    nr.autoname = 1
+    requests.append(nr)
+
+    volnum = 0
+    for (mntpt, fstype, minsize, maxsize, grow, format, asvol) in autoreq:
+        if fstype:
+            ptype = fsset.fileSystemTypeGet(fstype)
         else:
-            drvstr = drvstr + str + "\n"
-            str = ""
-            str = "%-*s" % (maxlen, "/dev/"+drive)
-    drvstr = drvstr + str + "\n"
-
-    rc = pomona.intf.messageWindow(_("Warning"), _(msg) % drvstr, type="yesno", default="no", custom_icon ="warning")
+            ptype = fsset.fileSystemTypeGetDefault()
 
-    return rc
+        if not asvol:
+            newrequest = partRequests.PartitionSpec(ptype,
+                                                    mountpoint = mntpt,
+                                                    size = minsize,
+                                                    maxSizeMB = maxsize,
+                                                    grow = grow,
+                                                    format = format)
+        else:
+            newrequest = partRequests.LogicalVolumeRequestSpec(ptype,
+                                                               mountpoint = mntpt,
+                                                               size = minsize,
+                                                               maxSizeMB = maxsize,
+                                                               grow = grow,
+                                                               format = format,
+                                                               lvname = "LogVol%02d" %(volnum,),
+                                                               volgroup = "lvm")
+            volnum += 1
 
-def setDefaultPartitioning(partitions, clear = CLEARPART_TYPE_ALL, doClear = 1):
-    autorequests = [ ("/", None, 1024, None, 1, 1, 1) ]
 
-    bootreq = getAutopartitionBoot()
-    if bootreq:
-        autorequests.extend(bootreq)
+        requests.append(newrequest)
 
-    (minswap, maxswap) = inutil.swapSuggestion()
-    autorequests.append((None, "swap", minswap, maxswap, 1, 1, 1))
+    return requests
 
-    if doClear:
-        partitions.autoClearPartType = clear
-        partitions.autoClearPartDrives = []
+def getAutopartitionBoot():
+    """Return the proper shorthand for the boot dir (arch dependent)."""
+    return [ ("/boot", None, 200, None, 0, 1, 0) ]
 
-    partitions.autoPartitionRequests = autoCreatePartitionRequests(autorequests)
 
-# XXX hack but these are common strings to TUI
+# XXX hack but these are common strings to TUI and GUI
 PARTMETHOD_TYPE_DESCR_TEXT = N_("Automatic Partitioning sets partitions "
-                                "based on the selected installation type. "
-                                "You also "
-                                "can customize the partitions once they "
-                                "have been created.\n\n"
-                                "The manual disk partitioning tool, Disk Druid, "
-                                "allows you "
-                                "to create partitions in an interactive "
-                                "environment. You can set the file system "
-                                "types, mount points, partition sizes, and more.")
+                               "based on the selected installation type. "
+                               "You also "
+                               "can customize the partitions once they "
+                               "have been created.\n\n"
+                               "The manual disk partitioning tool, Disk Druid, "
+                               "allows you "
+                               "to create partitions in an interactive "
+                               "environment. You can set the file system "
+                               "types, mount points, partition sizes, and more.")
 
 AUTOPART_DISK_CHOICE_DESCR_TEXT = N_("Before automatic partitioning can be "
                                      "set up by the installation program, you "
@@ -1140,8 +1637,5 @@ AUTOPART_DISK_CHOICE_DESCR_TEXT = N_("Before automatic partitioning can be "
                                      "your hard drives.")
 
 CLEARPART_TYPE_ALL_DESCR_TEXT = N_("Remove all partitions on this system")
-
-CLEARPART_TYPE_ALL_WARNING_MSG = N_("You have chosen to remove "
-                                    "all partitions (ALL DATA) on the "
-                                    "following drives:%s\nAre you sure you "
-                                    "want to do this?")
+CLEARPART_TYPE_LINUX_DESCR_TEXT = N_("Remove all Linux partitions on this system")
+CLEARPART_TYPE_NONE_DESCR_TEXT = N_("Keep all partitions and use existing free space")
index bb3f810d90dfce85854912a87fdda476a0023b22..6c29ae65e2159470775bf751e9efc107a6e40bd1 100644 (file)
@@ -15,7 +15,7 @@
 #
 
 import shutil
-import inutil
+import iutil
 import os, sys
 import logging
 from constants import *
index 0c0e72cb73533320ac17c80aa4831aad293a3d7f..90fffd6a903b26c059785fcbfdee88e2f265fac3 100644 (file)
@@ -1,5 +1,5 @@
 #
-# bootloader.py: anaconda bootloader shims
+# bootloader.py: pomona bootloader shims
 #
 # Erik Troan <ewt@redhat.com>
 # Jeremy Katz <katzj@redhat.com>
@@ -18,7 +18,7 @@ import isys
 import partedUtils
 import os
 import sys
-import inutil
+import iutil
 import string
 import crypt
 import random
@@ -154,7 +154,7 @@ class BootImages:
     def getDefault(self):
         return self.default
 
-    # XXX this has internal anaconda-ish knowledge.  ick
+    # XXX this has internal pomona-ish knowledge.  ick
     def setup(self, diskSet, fsset):
         devices = {}
         devs = self.availableBootDevices(diskSet, fsset)
@@ -181,7 +181,7 @@ class BootImages:
             if not label:
                 self.images[self.default] = ("linux", getProductName(), type)
 
-    # XXX more internal anaconda knowledge
+    # XXX more internal pomona knowledge
     def availableBootDevices(self, diskSet, fsset):
         devs = []
         foundDos = 0
index 3fded0188d26a5a6020b4ce72a7f1ebadaf0c053..5b625206eb652116ed410956a7aa9f9e6473e147 100644 (file)
@@ -27,22 +27,12 @@ DISPATCH_NOOP = None
 CLEARPART_TYPE_ALL = 0
 CLEARPART_TYPE_NONE = -1
 
-SOURCE_NOT_SET = -1
-SOURCE_CDROM = 0
-SOURCE_URL = 1
-SOURCE_HD = 2
-
 CB_UNDEF = -1
 CB_START = 0
 CB_STOP  = 1
 CB_WAIT  = 2
 CB_PROGRESS = 3
 
-exceptionText = _("An unhandled exception has occurred.  This "
-                  "is most likely a bug.  Please save a copy of "
-                  "the detailed exception and file a bug report "
-                  "against pomona at %s" %(bugurl,))
-
 INSTALL_OK = 0
 INSTALL_BACK = -1
 INSTALL_NOOP = -2
@@ -59,11 +49,27 @@ REQUEST_PREEXIST = 1
 REQUEST_NEW = 2
 REQUEST_RAID = 4
 REQUEST_PROTECTED = 8
+REQUEST_VG = 16 # volume group
+REQUEST_LV = 32 # logical volume
 
 # XXX this is made up and used by the size spinner; should just be set with
 # a callback
 MAX_PART_SIZE = 1024*1024*1024
 
+lvmErrorOutput = "/tmp/lvmout"
+
+exceptionText = _("An unhandled exception has occurred.  This "
+                  "is most likely a bug.  Please save a copy of "
+                  "the detailed exception and file a bug report")
+if not bugurl:
+    # this string will be combined with "An unhandled exception"...
+    # the leading space is not a typo.
+    exceptionText += _(" with the provider of this software.")
+else:
+    # this string will be combined with "An unhandled exception"...
+    # the leading space is not a typo.
+    exceptionText += _(" against pomona at %s") %(bugurl,)
+
 class Translator:
     """A simple class to facilitate on-the-fly translation for newt buttons"""
     def __init__(self, button, check):
@@ -105,6 +111,3 @@ TEXT_EDIT_CHECK = "edit"
 TEXT_EDIT_BUTTON = Translator(TEXT_EDIT_STR, TEXT_EDIT_CHECK)
 
 TEXT_F12_CHECK = "F12"
-
-TRUE = 1
-FALSE = 0
index 1f3a49843184e60f3b408884d6e4e4b25728d455..7ff178b914cd2dead5f262a976a39e800f8b018a 100644 (file)
@@ -1,5 +1,5 @@
 #
-# flags.py: global anaconda flags
+# flags.py: global pomona flags
 #
 # Copyright 2001 Red Hat, Inc.
 #
index 78f980cf0c38000cc4755d0e0b14bf18be7d76b3..5510eeed2f1717a6251b802ac5f4a3d76bcf55a5 100644 (file)
@@ -1,21 +1,30 @@
 #
 # fsset.py: filesystem management
 #
-# Matt Wilson <msw@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): Matt Wilson <msw@redhat.com>
+#            Jeremy Katz <katzj@redhat.com>
 #
 
+import math
 import string
 import isys
-import inutil
+import iutil
 import os
 import resource
 import posix
@@ -24,23 +33,21 @@ import errno
 import parted
 import sys
 import struct
-import time
-import partitioning
+import partitions
 import partedUtils
+import raid
+import lvm
+import time
 import types
-import math
 from flags import flags
+from constants import *
 
 import gettext
 _ = lambda x: gettext.ldgettext("pomona", x)
-N_ = lambda x: x
 
 import logging
 log = logging.getLogger("pomona")
 
-class BadBlocksError(Exception):
-    pass
-
 class SuspendError(Exception):
     pass
 
@@ -50,7 +57,7 @@ class OldSwapError(Exception):
 class ResizeError(Exception):
     pass
 
-defaultMountPoints = ['/', '/boot', '/home', '/tmp', '/usr', '/var', '/opt']
+defaultMountPoints = ['/', '/boot', '/home', '/tmp', '/usr', '/var', '/usr/local', '/opt']
 
 fileSystemTypes = {}
 
@@ -64,6 +71,7 @@ def fileSystemTypeGetDefault():
     else:
         raise ValueError, "You have neither ext4, ext3 or ext2 support in your kernel!"
 
+
 def fileSystemTypeGet(key):
     return fileSystemTypes[key]
 
@@ -77,7 +85,7 @@ def getUsableLinuxFs():
     rc = []
     for fsType in fileSystemTypes.keys():
         if fileSystemTypes[fsType].isMountable() and \
-                        fileSystemTypes[fsType].isLinuxNativeFS():
+               fileSystemTypes[fsType].isLinuxNativeFS():
             rc.append(fsType)
 
     # make sure the default is first in the list, kind of ugly
@@ -89,61 +97,19 @@ def getUsableLinuxFs():
     return rc
 
 def devify(device):
-    if device in ["proc", "devpts", "sysfs", "tmpfs"]:
+    if device in ["proc", "devpts", "sysfs", "tmpfs"] or device.find(":") != -1:
         return device
     elif device == "sys":
         return "sysfs"
     elif device == "shm":
         return "tmpfs"
+    elif device == "spufs":
+        return "spufs"
     elif device != "none" and device[0] != '/':
         return "/dev/" + device
     else:
         return device
 
-class LabelFactory:
-    def __init__(self):
-        self.labels = None
-
-    def createLabel(self, mountpoint, maxLabelChars):
-        if self.labels == None:
-            self.labels = {}
-            diskset = partedUtils.DiskSet()
-            diskset.openDevices()
-            labels = diskset.getLabels()
-            del diskset
-            self.reserveLabels(labels)
-
-        if len(mountpoint) > maxLabelChars:
-            mountpoint = mountpoint[0:maxLabelChars]
-        count = 0
-        while self.labels.has_key(mountpoint):
-            count = count + 1
-            s = "%s" % count
-            if (len(mountpoint) + len(s)) <= maxLabelChars:
-                mountpoint = mountpoint + s
-            else:
-                strip = len(mountpoint) + len(s) - maxLabelChars
-                mountpoint = mountpoint[0:len(mountpoint) - strip] + s
-        self.labels[mountpoint] = 1
-
-        return mountpoint
-
-    def reserveLabels(self, labels):
-        if self.labels == None:
-            self.labels = {}
-        for device, label in labels.items():
-            self.labels[label] = 1
-
-    def isLabelReserved(self, label):
-        if self.labels == None:
-            return False
-        elif self.labels.has_key(label):
-            return True
-        else:
-            return False
-
-labelFactory = LabelFactory()
-
 class FileSystemType:
     kernelFilesystems = {}
     lostAndFoundContext = None
@@ -162,6 +128,8 @@ class FileSystemType:
         self.migratetofs = None
         self.extraFormatArgs = []
         self.maxLabelChars = 16
+        self.packages = []
+        self.needProgram = []
         self.resizable = False
         self.supportsFsProfiles = False
         self.fsProfileSpecifier = None
@@ -198,7 +166,7 @@ class FileSystemType:
               instroot=""):
         if not self.isMountable():
             return
-        inutil.mkdirChain("%s/%s" %(instroot, mountpoint))
+        iutil.mkdirChain("%s/%s" %(instroot, mountpoint))
         log.debug("mounting %s on %s/%s as %s" %(device, instroot,
                                                  mountpoint, self.getMountName()))
         isys.mount(device, "%s/%s" %(instroot, mountpoint),
@@ -220,6 +188,9 @@ class FileSystemType:
     def getMountName(self, quoted = 0):
         return self.getName(quoted)
 
+    def getNeededPackages(self):
+        return self.packages
+
     def registerDeviceArgumentFunction(self, klass, function):
         self.deviceArguments[klass] = function
 
@@ -279,6 +250,12 @@ class FileSystemType:
         return FileSystemType.kernelFilesystems.has_key(self.getMountName()) or self.getName() == "auto"
 
     def isSupported(self):
+        # check to ensure we have the binaries they need
+        for p in self.needProgram:
+            if len(filter(lambda d: os.path.exists("%s/%s" %(d, p)),
+                          os.environ["PATH"].split(":"))) == 0:
+                return False
+
         if self.supported == -1:
             return self.isMountable()
         return self.supported
@@ -330,33 +307,46 @@ class reiserfsFileSystem(FileSystemType):
         self.formattable = 1
         self.checked = 1
         self.linuxnativefs = 1
-        self.supported = -1
+        self.bootable = True
+        # this is totally, 100% unsupported.  Boot with "linux reiserfs"
+        # at the boot: prompt will let you make new reiserfs filesystems
+        # in the installer.  Bugs filed when you use this will be closed
+        # WONTFIX.
+        if flags.cmdline.has_key("reiserfs"):
+            self.supported = -1
+        else:
+            self.supported = 0
+
         self.name = "reiserfs"
+        self.packages = [ "reiserfs-utils" ]
+        self.needProgram = [ "mkreiserfs", "reiserfstune" ]
 
         self.maxSizeMB = 8 * 1024 * 1024
 
     def formatDevice(self, entry, progress, chroot='/'):
         devicePath = entry.device.setupDevice(chroot)
+
         p = os.pipe()
         os.write(p[1], "y\n")
         os.close(p[1])
 
-        rc = inutil.execWithRedirect("mkreiserfs",
-                                     [devicePath],
-                                     stdin = p[0],
-                                     stdout = "/dev/tty5",
-                                     stderr = "/dev/tty5", searchPath = 1)
+        rc = iutil.execWithRedirect("mkreiserfs",
+                                    [devicePath],
+                                    stdin = p[0],
+                                    stdout = "/dev/tty5",
+                                    stderr = "/dev/tty5", searchPath = 1)
 
         if rc:
             raise SystemError
 
     def labelDevice(self, entry, chroot):
         devicePath = entry.device.setupDevice(chroot)
-        label = labelFactory.createLabel(entry.mountpoint, self.maxLabelChars)
-        rc = inutil.execWithRedirect("reiserfstune",
-                                     ["--label", label, devicePath],
-                                     stdout = "/dev/tty5",
-                                     stderr = "/dev/tty5", searchPath = 1)
+        label = self.createLabel(entry.mountpoint, self.maxLabelChars,
+                                 kslabel = entry.label)
+        rc = iutil.execWithRedirect("reiserfstune",
+                                    ["--label", label, devicePath],
+                                    stdout = "/dev/tty5",
+                                    stderr = "/dev/tty5", searchPath = 1)
         if rc:
             raise SystemError
         entry.setLabel(label)
@@ -374,33 +364,116 @@ class xfsFileSystem(FileSystemType):
         self.maxSizeMB = 16 * 1024 * 1024
         self.maxLabelChars = 12
         self.supported = -1
+        if not os.path.exists("/sbin/mkfs.xfs") and not os.path.exists("/usr/sbin/mkfs.xfs"):
+            self.supported = 0
+
+        self.packages = [ "xfsprogs" ]
+        self.needProgram = [ "mkfs.xfs", "xfs_admin" ]
 
     def formatDevice(self, entry, progress, chroot='/'):
         devicePath = entry.device.setupDevice(chroot)
 
-        rc = inutil.execWithRedirect("mkfs.xfs",
-                                     ["-f", "-l", "internal",
-                                      "-i", "attr=2", devicePath],
-                                     stdout = "/dev/tty5",
-                                     stderr = "/dev/tty5", searchPath = 1)
+        rc = iutil.execWithRedirect("mkfs.xfs", ["-f", devicePath],
+                                    stdout = "/dev/tty5",
+                                    stderr = "/dev/tty5", searchPath = 1)
 
         if rc:
             raise SystemError
 
     def labelDevice(self, entry, chroot):
         devicePath = entry.device.setupDevice(chroot)
-        label = labelFactory.createLabel(entry.mountpoint, self.maxLabelChars)
-        db_cmd = "label " + label
-        rc = inutil.execWithRedirect("xfs_db",
-                                     ["-x", "-c", db_cmd, devicePath],
-                                     stdout = "/dev/tty5",
-                                     stderr = "/dev/tty5", searchPath = 1)
+        label = self.createLabel(entry.mountpoint, self.maxLabelChars,
+                                 kslabel = entry.label)
+        rc = iutil.execWithRedirect("xfs_admin",
+                                    ["-L", label, devicePath],
+                                    stdout = "/dev/tty5",
+                                    stderr = "/dev/tty5", searchPath = 1)
         if rc:
             raise SystemError
         entry.setLabel(label)
 
 fileSystemTypeRegister(xfsFileSystem())
 
+class jfsFileSystem(FileSystemType):
+    def __init__(self):
+        FileSystemType.__init__(self)
+        self.partedFileSystemType = parted.file_system_type_get("jfs")
+        self.formattable = 1
+        self.checked = 1
+        self.linuxnativefs = 1
+        self.maxLabelChars = 16
+        self.bootable = True
+        # this is totally, 100% unsupported.  Boot with "linux jfs"
+        # at the boot: prompt will let you make new reiserfs filesystems
+        # in the installer.  Bugs filed when you use this will be closed
+        # WONTFIX.
+        if flags.cmdline.has_key("jfs"):
+            self.supported = -1
+        else:
+            self.supported = 0
+
+        self.name = "jfs"
+        self.packages = [ "jfsutils" ]
+        self.needProgram = [ "mkfs.jfs", "jfs_tune" ]
+
+        self.maxSizeMB = 8 * 1024 * 1024
+
+    def labelDevice(self, entry, chroot):
+        devicePath = entry.device.setupDevice(chroot)
+        label = self.createLabel(entry.mountpoint, self.maxLabelChars,
+                                 kslabel = entry.label)
+        rc = iutil.execWithRedirect("jfs_tune",
+                                   ["-L", label, devicePath],
+                                    stdout = "/dev/tty5",
+                                    stderr = "/dev/tty5", searchPath = 1)
+        if rc:
+            raise SystemError
+        entry.setLabel(label)
+
+    def formatDevice(self, entry, progress, chroot='/'):
+        devicePath = entry.device.setupDevice(chroot)
+
+        rc = iutil.execWithRedirect("mkfs.jfs",
+                                    ["-q", devicePath],
+                                    stdout = "/dev/tty5",
+                                    stderr = "/dev/tty5", searchPath = 1)
+
+        if rc:
+            raise SystemError
+
+fileSystemTypeRegister(jfsFileSystem())
+
+class gfs2FileSystem(FileSystemType):
+    def __init__(self):
+        FileSystemType.__init__(self)
+        self.partedFileSystemType = None
+        self.formattable = 1
+        self.checked = 1
+        self.linuxnativefs = 1
+        if flags.cmdline.has_key("gfs2"):
+            self.supported = -1
+        else:
+            self.supported = 0
+
+        self.name = "gfs2"
+        self.packages = [ "gfs2-utils" ]
+        self.needProgram = [ "mkfs.gfs2" ]
+
+        self.maxSizeMB = 8 * 1024 * 1024
+
+    def formatDevice(self, entry, progress, chroot='/'):
+        devicePath = entry.device.setupDevice(chroot)
+        rc = iutil.execWithRedirect("mkfs.gfs2",
+                                    ["-j", "1", "-p", "lock_nolock",
+                                     "-O", devicePath],
+                                    stdout = "/dev/tty5",
+                                    stderr = "/dev/tty5", searchPath = 1)
+
+        if rc:
+            raise SystemError
+
+fileSystemTypeRegister(gfs2FileSystem())
+
 class extFileSystem(FileSystemType):
     def __init__(self):
         FileSystemType.__init__(self)
@@ -409,6 +482,7 @@ class extFileSystem(FileSystemType):
         self.checked = 1
         self.linuxnativefs = 1
         self.maxSizeMB = 8 * 1024 * 1024
+        self.packages = [ "e2fsprogs" ]
         self.supportsFsProfiles = True
         self.fsProfileSpecifier = "-T"
         self.resizable = True
@@ -424,7 +498,7 @@ class extFileSystem(FileSystemType):
                          _("Checking filesystem on %s...") %(devicePath),
                          100, pulse = True)
 
-        rc = inutil.execWithPulseProgress("e2fsck", ["-f", "-p", "-C", "0", devicePath],
+        rc = iutil.execWithPulseProgress("e2fsck", ["-f", "-p", "-C", "0", devicePath],
                                          stdout="/tmp/resize.out",
                                          stderr="/tmp/resize.out",
                                          progress = w)
@@ -437,7 +511,7 @@ class extFileSystem(FileSystemType):
                          100, pulse = True)
 
         log.info("resizing %s" %(devicePath,))
-        rc = inutil.execWithPulseProgress("resize2fs",
+        rc = iutil.execWithPulseProgress("resize2fs",
                                          ["-p", devicePath, "%sM" %(size,)],
                                          stdout="/tmp/resize.out",
                                          stderr="/tmp/resize.out",
@@ -452,7 +526,7 @@ class extFileSystem(FileSystemType):
         devicePath = "/dev/%s" % (device,)
 
         # FIXME: it'd be nice if we didn't have to parse this out ourselves
-        buf = inutil.execWithCapture("dumpe2fs",
+        buf = iutil.execWithCapture("dumpe2fs",
                                     ["-h", devicePath],
                                     stderr = "/dev/tty5")
         blocks = free = bs = 0
@@ -493,7 +567,7 @@ class extFileSystem(FileSystemType):
         label = self.createLabel(entry.mountpoint, self.maxLabelChars,
                                  kslabel = entry.label)
 
-        rc = inutil.execWithRedirect("e2label",
+        rc = iutil.execWithRedirect("e2label",
                                     [devicePath, label],
                                     stdout = "/dev/tty5",
                                     stderr = "/dev/tty5", searchPath = 1)
@@ -533,7 +607,7 @@ class extFileSystem(FileSystemType):
         if not isys.ext2HasJournal(devicePath):
             return
 
-        rc = inutil.execWithRedirect("tune2fs",
+        rc = iutil.execWithRedirect("tune2fs",
                                     ["-c0", "-i0",
                                      "-ouser_xattr,acl", devicePath],
                                     stdout = "/dev/tty5",
@@ -561,7 +635,7 @@ class ext2FileSystem(extFileSystem):
             log.info("Skipping migration of %s, has a journal already.\n" % devicePath)
             return
 
-        rc = inutil.execWithRedirect("tune2fs",
+        rc = iutil.execWithRedirect("tune2fs",
                                     ["-j", devicePath ],
                                     stdout = "/dev/tty5",
                                     stderr = "/dev/tty5", searchPath = 1)
@@ -597,7 +671,8 @@ class ext3FileSystem(extFileSystem):
         self.name = "ext3"
         self.extraFormatArgs = [ "-t", "ext3" ]
         self.partedFileSystemType = parted.file_system_type_get("ext3")
-        self.migratetofs = ['ext4dev']
+        if flags.cmdline.has_key("ext4"):
+            self.migratetofs = ['ext4dev']
 
     def formatDevice(self, entry, progress, chroot='/'):
         extFileSystem.formatDevice(self, entry, progress, chroot)
@@ -614,7 +689,7 @@ class ext3FileSystem(extFileSystem):
                                  "than ext4")
 
         # This is only needed as long as ext4 is actually "ext4dev"
-        rc = inutil.execWithRedirect("tune2fs",
+        rc = iutil.execWithRedirect("tune2fs",
                                     ["-E", "test_fs", devicePath ],
                                     stdout = "/dev/tty5",
                                     stderr = "/dev/tty5", searchPath = 1)
@@ -626,17 +701,90 @@ fileSystemTypeRegister(ext3FileSystem())
 class ext4FileSystem(extFileSystem):
     def __init__(self):
         extFileSystem.__init__(self)
-        self.name = "ext4"
+        self.name = "ext4dev"
         self.partedFileSystemType = parted.file_system_type_get("ext3")
-        self.extraFormatArgs = [ "-t", "ext4" ]
+        self.extraFormatArgs = [ "-t", "ext4dev" ]
         self.bootable = False
 
+        # this is way way experimental at present...
+        if flags.cmdline.has_key("ext4"):
+            self.supported = -1
+        else:
+            self.supported = 0
+
+
     def formatDevice(self, entry, progress, chroot='/'):
         extFileSystem.formatDevice(self, entry, progress, chroot)
         extFileSystem.setExt3Options(self, entry, progress, chroot)
 
 fileSystemTypeRegister(ext4FileSystem())
 
+class raidMemberDummyFileSystem(FileSystemType):
+    def __init__(self):
+        FileSystemType.__init__(self)
+        self.partedFileSystemType = parted.file_system_type_get("ext2")
+        self.partedPartitionFlags = [ parted.PARTITION_RAID ]
+        self.formattable = 1
+        self.checked = 0
+        self.linuxnativefs = 1
+        self.name = "software RAID"
+        self.maxSizeMB = 8 * 1024 * 1024
+        self.supported = 1
+
+        if len(raid.availRaidLevels) == 0:
+            self.supported = 0
+
+        self.packages = [ "mdadm" ]
+
+    def formatDevice(self, entry, progress, chroot='/'):
+        # mkraid did all we need to format this partition...
+        pass
+
+fileSystemTypeRegister(raidMemberDummyFileSystem())
+
+class lvmPhysicalVolumeDummyFileSystem(FileSystemType):
+    def __init__(self):
+        FileSystemType.__init__(self)
+        self.partedFileSystemType = parted.file_system_type_get("ext2")
+        self.partedPartitionFlags = [ parted.PARTITION_LVM ]
+        self.formattable = 1
+        self.checked = 0
+        self.linuxnativefs = 1
+        self.name = "physical volume (LVM)"
+        self.maxSizeMB = 8 * 1024 * 1024
+        self.supported = 1
+        self.packages = [ "lvm2" ]
+
+    def isMountable(self):
+        return 0
+
+    def formatDevice(self, entry, progress, chroot='/'):
+        # already done by the pvcreate during volume creation
+        pass
+
+fileSystemTypeRegister(lvmPhysicalVolumeDummyFileSystem())
+
+class lvmVolumeGroupDummyFileSystem(FileSystemType):
+    def __init__(self):
+        FileSystemType.__init__(self)
+        self.partedFileSystemType = parted.file_system_type_get("ext2")
+        self.formattable = 1
+        self.checked = 0
+        self.linuxnativefs = 0
+        self.name = "volume group (LVM)"
+        self.supported = 0
+        self.maxSizeMB = 8 * 1024 * 1024
+        self.packages = [ "lvm2" ]
+
+    def isMountable(self):
+        return 0
+
+    def formatDevice(self, entry, progress, chroot='/'):
+        # the vgcreate already did this
+        pass
+
+fileSystemTypeRegister(lvmVolumeGroupDummyFileSystem())
+
 class swapFileSystem(FileSystemType):
     enabledSwaps = {}
 
@@ -650,7 +798,8 @@ class swapFileSystem(FileSystemType):
         self.supported = 1
         self.maxLabelChars = 15
 
-    def mount(self, device, mountpoint, readOnly=0, bindMount=0, instroot = None):
+    def mount(self, device, mountpoint, readOnly=0, bindMount=0,
+              instroot = None):
         pagesize = resource.getpagesize()
         buf = None
         if pagesize > 2048:
@@ -675,7 +824,7 @@ class swapFileSystem(FileSystemType):
             if sig == 'S1SUSPEND\x00' or sig == 'S2SUSPEND\x00':
                 raise SuspendError
 
-        isys.swapon(device)
+        isys.swapon (device)
 
     def umount(self, device, path):
         # unfortunately, turning off swap is bad.
@@ -683,8 +832,8 @@ class swapFileSystem(FileSystemType):
 
     def formatDevice(self, entry, progress, chroot='/'):
         file = entry.device.setupDevice(chroot)
-        rc = inutil.execWithRedirect("mkswap",
-                                     ["-v1", file],
+        rc = iutil.execWithRedirect ("mkswap",
+                                     ['-v1', file],
                                      stdout = "/dev/tty5",
                                      stderr = "/dev/tty5",
                                      searchPath = 1)
@@ -702,9 +851,9 @@ class swapFileSystem(FileSystemType):
             swapLabel = "SWAP-%s" % (devName[7:],)
         else:
             swapLabel = "SWAP-%s" % (devName)
-        label = labelFactory.createLabel(swapLabel, self.maxLabelChars)
-        rc = inutil.execWithRedirect("mkswap",
-                                     ["-v1", "-L", label, file],
+        label = self.createLabel(swapLabel, self.maxLabelChars)
+        rc = iutil.execWithRedirect ("mkswap",
+                                     ['-v1', "-L", label, file],
                                      stdout = "/dev/tty5",
                                      stderr = "/dev/tty5",
                                      searchPath = 1)
@@ -733,10 +882,46 @@ class FATFileSystem(FileSystemType):
     def __init__(self):
         FileSystemType.__init__(self)
         self.partedFileSystemType = parted.file_system_type_get("fat32")
-        self.formattable = 0
+        self.formattable = 1
         self.checked = 0
         self.maxSizeMB = 1024 * 1024
         self.name = "vfat"
+        self.packages = [ "dosfstools" ]
+        self.defaultOptions = "umask=0077,shortname=winnt"
+
+    def formatDevice(self, entry, progress, chroot='/'):
+        devicePath = entry.device.setupDevice(chroot)
+        devArgs = self.getDeviceArgs(entry.device)
+        args = [ devicePath ]
+        args.extend(devArgs)
+
+        rc = iutil.execWithRedirect("mkdosfs", args,
+                                    stdout = "/dev/tty5",
+                                    stderr = "/dev/tty5", searchPath = 1)
+        if rc:
+            raise SystemError
+
+    def labelDevice(self, entry, chroot):
+        devicePath = entry.device.setupDevice(chroot)
+        label = self.createLabel(entry.mountpoint, self.maxLabelChars,
+                                 kslabel = entry.label)
+
+        rc = iutil.execWithRedirect("dosfslabel",
+                                    [devicePath, label],
+                                    stdout = "/dev/tty5",
+                                    stderr = "/dev/tty5",
+                                    searchPath = 1)
+        if rc:
+            msg = iutil.execWithCapture("dosfslabel", [devicePath],
+                                        stderr="/dev/tty5")
+            raise SystemError, "dosfslabel failed on device %s: %s" % (devicePath, msg)
+
+        newLabel = iutil.execWithCapture("dosfslabel", [devicePath],
+                                         stderr = "/dev/tty5")
+        newLabel = newLabel.strip()
+        if label != newLabel:
+            raise SystemError, "dosfslabel failed on device %s" % (devicePath,)
+        entry.setLabel(label)
 
 fileSystemTypeRegister(FATFileSystem())
 
@@ -747,9 +932,120 @@ class NTFSFileSystem(FileSystemType):
         self.formattable = 0
         self.checked = 0
         self.name = "ntfs"
+        if len(filter(lambda d: os.path.exists("%s/ntfsresize" %(d,)),
+                      os.environ["PATH"].split(":"))) > 0:
+            self.resizable = True
+
+    def resize(self, entry, size, progress, chroot='/'):
+        devicePath = entry.device.setupDevice(chroot)
+        log.info("resizing %s to %sM" %(devicePath, size))
+        w = None
+        if progress:
+            w = progress(_("Resizing"),
+                         _("Resizing filesystem on %s...") %(devicePath),
+                         100, pulse = True)
+
+        p = os.pipe()
+        os.write(p[1], "y\n")
+        os.close(p[1])
+
+        # FIXME: we should call ntfsresize -c to ensure that we can resize
+        # before starting the operation
+
+        rc = iutil.execWithPulseProgress("ntfsresize", ["-v",
+                                                        "-s", "%sM" %(size,),
+                                                        devicePath],
+                                         stdin = p[0],
+                                         stdout = "/tmp/resize.out",
+                                         stderr = "/tmp/resize.out",
+                                         progress = w)
+        if progress:
+            w.pop()
+        if rc:
+            raise ResizeError, ("Resize of %s failed" %(devicePath,), devicePath)
+
+    def getMinimumSize(self, device):
+        """Return the minimum filesystem size in megabytes"""
+        devicePath = "/dev/%s" % (device,)
+
+        buf = iutil.execWithCapture("ntfsresize", ["-m", devicePath],
+                                    stderr = "/dev/tty5")
+        for l in buf.split("\n"):
+            if not l.startswith("Minsize"):
+                continue
+            try:
+                min = l.split(":")[1].strip()
+                return int(min) + 250
+            except Exception, e:
+                log.warning("Unable to parse output for minimum size on %s: %s" %(device, e))
+
+        log.warning("Unable to discover minimum size of filesystem on %s" %(device,))
+        return 1
+
 
 fileSystemTypeRegister(NTFSFileSystem())
 
+class hfsFileSystem(FileSystemType):
+    def __init__(self):
+        FileSystemType.__init__(self)
+        self.partedFileSystemType = parted.file_system_type_get("hfs")
+        self.formattable = 1
+        self.checked = 0
+        self.name = "hfs"
+        self.supported = 0
+        self.needProgram = [ "hformat" ]
+
+    def isMountable(self):
+        return 0
+
+    def formatDevice(self, entry, progress, chroot='/'):
+        devicePath = entry.device.setupDevice(chroot)
+        devArgs = self.getDeviceArgs(entry.device)
+        args = [ devicePath ]
+        args.extend(devArgs)
+
+        rc = iutil.execWithRedirect("hformat", args,
+                                    stdout = "/dev/tty5",
+                                    stderr = "/dev/tty5", searchPath = 1)
+        if rc:
+            raise SystemError
+
+fileSystemTypeRegister(hfsFileSystem())
+
+class HfsPlusFileSystem(FileSystemType):
+    def __init__(self):
+        FileSystemType.__init__(self)
+        self.partedFileSystemType = parted.file_system_type_get("hfs+")
+        self.formattable = 0
+        self.checked = 0
+        self.name = "hfs+"
+
+fileSystemTypeRegister(HfsPlusFileSystem())
+
+class networkFileSystem(FileSystemType):
+    def __init__(self):
+        FileSystemType.__init__(self)
+        self.formattable = 0
+        self.checked = 0
+        self.name = "nfs"
+
+    def isMountable(self):
+        return 0
+
+fileSystemTypeRegister(networkFileSystem())
+
+class nfsv4FileSystem(FileSystemType):
+    def __init__(self):
+        FileSystemType.__init__(self)
+        self.formattable = 0
+        self.checked = 0
+        self.name = "nfs4"
+
+    def isMountable(self):
+        return 0
+
+fileSystemTypeRegister(nfsv4FileSystem())
+
 class ForeignFileSystem(FileSystemType):
     def __init__(self):
         FileSystemType.__init__(self)
@@ -762,7 +1058,7 @@ class ForeignFileSystem(FileSystemType):
 
 fileSystemTypeRegister(ForeignFileSystem())
 
-class PsudoFileSystem(FileSystemType):
+class PseudoFileSystem(FileSystemType):
     def __init__(self, name):
         FileSystemType.__init__(self)
         self.formattable = 0
@@ -773,21 +1069,27 @@ class PsudoFileSystem(FileSystemType):
     def isKernelFS(self):
         return True
 
-class ProcFileSystem(PsudoFileSystem):
+class SpuFileSystem(PseudoFileSystem):
     def __init__(self):
-        PsudoFileSystem.__init__(self, "proc")
+        PseudoFileSystem.__init__(self, "spufs")
+
+fileSystemTypeRegister(SpuFileSystem())
+
+class ProcFileSystem(PseudoFileSystem):
+    def __init__(self):
+        PseudoFileSystem.__init__(self, "proc")
 
 fileSystemTypeRegister(ProcFileSystem())
 
-class SysfsFileSystem(PsudoFileSystem):
+class SysfsFileSystem(PseudoFileSystem):
     def __init__(self):
-        PsudoFileSystem.__init__(self, "sysfs")
+        PseudoFileSystem.__init__(self, "sysfs")
 
 fileSystemTypeRegister(SysfsFileSystem())
 
-class DevptsFileSystem(PsudoFileSystem):
+class DevptsFileSystem(PseudoFileSystem):
     def __init__(self):
-        PsudoFileSystem.__init__(self, "devpts")
+        PseudoFileSystem.__init__(self, "devpts")
         self.defaultOptions = "gid=5,mode=620"
 
     def isMountable(self):
@@ -795,35 +1097,37 @@ class DevptsFileSystem(PsudoFileSystem):
 
 fileSystemTypeRegister(DevptsFileSystem())
 
-class DevshmFileSystem(PsudoFileSystem):
+class DevshmFileSystem(PseudoFileSystem):
     def __init__(self):
-        PsudoFileSystem.__init__(self, "tmpfs")
+        PseudoFileSystem.__init__(self, "tmpfs")
 
     def isMountable(self):
         return 0
 
 fileSystemTypeRegister(DevshmFileSystem())
 
-class AutoFileSystem(PsudoFileSystem):
+class AutoFileSystem(PseudoFileSystem):
     def __init__(self):
-        PsudoFileSystem.__init__(self, "auto")
+        PseudoFileSystem.__init__(self, "auto")
 
-    def mount(self, device, mountpoint, readOnly=0, bindMount=0, instroot = None):
+    def mount(self, device, mountpoint, readOnly=0, bindMount=0,
+              instroot = None):
         errNum = 0
         errMsg = "cannot mount auto filesystem on %s of this type" % device
 
         if not self.isMountable():
             return
-        inutil.mkdirChain("%s/%s" %(instroot, mountpoint))
-        for fs in getFStoTry (device):
+        iutil.mkdirChain("%s/%s" %(instroot, mountpoint))
+
+        fs = isys.readFSType(device)
+        if fs is not None:
             try:
-                isys.mount (device, mountpoint, fstype = fs, readOnly = readOnly,
-                        bindMount = bindMount)
+                isys.mount (device, mountpoint, fstype = fs, readOnly =
+                            readOnly, bindMount = bindMount)
                 return
             except SystemError, (num, msg):
                 errNum = num
                 errMsg = msg
-                continue
 
         raise SystemError (errNum, errMsg)
 
@@ -832,9 +1136,9 @@ class AutoFileSystem(PsudoFileSystem):
 
 fileSystemTypeRegister(AutoFileSystem())
 
-class BindFileSystem(PsudoFileSystem):
+class BindFileSystem(PseudoFileSystem):
     def __init__(self):
-        PsudoFileSystem.__init__(self, "bind")
+        PseudoFileSystem.__init__(self, "bind")
 
     def isMountable(self):
         return 1
@@ -866,16 +1170,16 @@ class FileSystemSet:
     def reset (self):
         self.entries = []
         proc = FileSystemSetEntry(Device(device="proc"), '/proc',
-                                                                                                                fileSystemTypeGet("proc"))
+                                  fileSystemTypeGet("proc"))
         self.add(proc)
         sys = FileSystemSetEntry(Device(device="sys"), '/sys',
-                                                                                                        fileSystemTypeGet("sysfs"))
+                                 fileSystemTypeGet("sysfs"))
         self.add(sys)
         pts = FileSystemSetEntry(Device(device="devpts"), '/dev/pts',
-                                                                                                        fileSystemTypeGet("devpts"), "gid=5,mode=620")
+                                 fileSystemTypeGet("devpts"), "gid=5,mode=620")
         self.add(pts)
         shm = FileSystemSetEntry(Device(device="shm"), '/dev/shm',
-                                                                                                        fileSystemTypeGet("tmpfs"))
+                                 fileSystemTypeGet("tmpfs"))
         self.add(shm)
 
     def verify (self):
@@ -886,36 +1190,42 @@ class FileSystemSet:
     def add (self, newEntry):
         # Should object A be sorted after object B?  Take mountpoints and
         # device names into account so bind mounts are sorted correctly.
-        def comesAfter(a, b):
+        def comesAfter (a, b):
             mntA = a.mountpoint
             mntB = b.mountpoint
             devA = a.device.getDevice()
             devB = b.device.getDevice()
 
-            if not mntB or not devB:
-                return True
-            if not mntA or not devA:
+            if not mntB:
                 return False
-
-            if (mntA.startswith(mntB) and mntA != mntB) or (devA.startswith(mntB) and devA != devB):
+            if mntA and mntA != mntB and mntA.startswith(mntB):
                 return True
-            else:
-                return False
+            if devA and devA != mntB and devA.startswith(mntB):
+                return True
+            return False
+
+        def samePseudo (a, b):
+            return isinstance(a.fsystem, PseudoFileSystem) and isinstance (b.fsystem, PseudoFileSystem) and \
+                   not isinstance (a.fsystem, BindFileSystem) and not isinstance (b.fsystem, BindFileSystem) and \
+                   a.fsystem.getName() == b.fsystem.getName()
+
+        def sameEntry (a, b):
+            return a.device.getDevice() == b.device.getDevice() and a.mountpoint == b.mountpoint
 
         # Remove preexisting duplicate entries - pseudo filesystems are
         # duplicate if they have the same filesystem type as an existing one.
         # Otherwise, they have to have the same device and mount point
         # (required to check for bind mounts).
         for existing in self.entries:
-            if (isinstance (newEntry.fsystem, PsudoFileSystem) and existing.fsystem.getName() == newEntry.fsystem.getName()) or (existing.device.getDevice() == newEntry.device.getDevice() and existing.mountpoint == newEntry.mountpoint):
+            if samePseudo (newEntry, existing) or sameEntry (newEntry, existing):
                 self.remove(existing)
 
-                ### debuggin'
-                #log.info ("fsset at %s\n"
-                #          "adding entry for %s\n"
-                #          "entry object %s, class __dict__ is %s",
-                #          self, entry.mountpoint, entry,
-                #          isys.printObject(entry.__dict__))
+        # XXX debuggin'
+##         log.info ("fsset at %s\n"
+##                   "adding entry for %s\n"
+##                   "entry object %s, class __dict__ is %s",
+##                   self, entry.mountpoint, entry,
+##                   isys.printObject(entry.__dict__))
 
         insertAt = 0
 
@@ -948,12 +1258,17 @@ class FileSystemSet:
         for entry in self.entries:
             if entry.device.getDevice() == dev:
                 return entry
+
+            # getDevice() will return the mapped device if using LUKS
+            if entry.device.device == dev:
+                return entry
+
         return None
 
-    def copy(self):
+    def copy (self):
         new = FileSystemSet()
         for entry in self.entries:
-            new.add(entry)
+            new.add (entry)
         return new
 
     def fstab (self):
@@ -983,7 +1298,7 @@ class FileSystemSet:
                                           entry.order)
         return fstab
 
-    def mtab(self):
+    def mtab (self):
         format = "%s %s %s %s 0 0\n"
         mtab = ""
         for entry in self.entries:
@@ -993,8 +1308,9 @@ class FileSystemSet:
                 # swap doesn't end up in the mtab
                 if entry.fsystem.getName() == "swap":
                     continue
-                if entry.options:
-                    options = "rw," + entry.options
+                options = entry.getOptions()
+                if options:
+                    options = "rw," + options
                 else:
                     options = "rw"
                 mtab = mtab + format % (devify(entry.device.getDevice()),
@@ -1003,24 +1319,71 @@ class FileSystemSet:
                                         options)
         return mtab
 
+    def raidtab(self):
+        # set up raidtab...
+        raidtab = ""
+        for entry in self.entries:
+            if entry.device.getName() == "RAIDDevice":
+                raidtab = raidtab + entry.device.raidTab()
+
+        return raidtab
+
+    def mdadmConf(self):
+        """Make the mdadm.conf file with mdadm command.
+
+        This creates a conf file with active arrays.  In other words
+        the arrays that we don't want included must be inactive.
+        """
+        activeArrays = iutil.execWithCapture("mdadm", ["--detail", "--scan"])
+        if len(activeArrays) == 0:
+            return
+
+        cf = """
+# mdadm.conf written out by pomona
+DEVICE partitions
+MAILADDR root
+
+%s
+""" % activeArrays
+        return cf
+
+    def crypttab(self):
+        """set up /etc/crypttab"""
+        crypttab = ""
+        for entry in self.entries:
+            if entry.device.crypto:
+                crypttab += entry.device.crypto.crypttab()
+
+        return crypttab
+
+    def write (self, prefix):
+        f = open (prefix + "/etc/fstab", "w")
+        f.write (self.fstab())
+        f.close ()
+
+        cf = self.mdadmConf()
+
+        if cf:
+            f = open (prefix + "/etc/mdadm.conf", "w")
+            f.write (cf)
+            f.close ()
 
-    def write(self, prefix):
-        f = open(prefix + "/etc/fstab", "w")
-        f.write(self.fstab())
-        f.close()
+        crypttab = self.crypttab()
+        if crypttab:
+            f = open(prefix + "/etc/crypttab", "w")
+            f.write(crypttab)
+            f.close()
 
         # touch mtab
-        open(prefix + "/etc/mtab", "w+")
-        f.close()
+        open (prefix + "/etc/mtab", "w+")
+        f.close ()
 
-    ## XXX
     def mkDevRoot(self, instPath):
         root = self.getEntryByMountPoint("/")
         dev = "%s/dev/%s" % (instPath, root.device.getDevice())
-        rdev = os.stat(dev).st_rdev
-
-        #if not os.path.exists("%s/dev/root" %(instPath,)):
-        #       os.mknod("%s/dev/root" % (instPath,), stat.S_IFBLK | 0600, rdev)
+        if not os.path.exists("%s/dev/root" %(instPath,)) and os.path.exists(dev):
+            rdev = os.stat(dev).st_rdev
+            os.mknod("%s/dev/root" % (instPath,), stat.S_IFBLK | 0600, rdev)
 
     # return the "boot" device
     def getBootDev(self):
@@ -1029,6 +1392,8 @@ class FileSystemSet:
         for entry in self.entries:
             mntDict[entry.mountpoint] = entry.device
 
+        # FIXME: this ppc stuff feels kind of crufty -- the abstraction
+        # here needs a little bit of work
         if mntDict.has_key("/boot"):
             bootDev = mntDict['/boot']
         elif mntDict.has_key("/"):
@@ -1044,8 +1409,12 @@ class FileSystemSet:
             log.warning("no boot device set")
             return ret
 
+        if bootDev.getName() == "RAIDDevice":
+            ret['boot'] = (bootDev.device, N_("RAID Device"))
+            return ret
+
         ret['boot'] = (bootDev.device, N_("First sector of boot partition"))
-        ret['mbr']  = (bl.drivelist[0], N_("Master Boot Record (MBR)"))
+        ret['mbr'] = (bl.drivelist[0], N_("Master Boot Record (MBR)"))
         return ret
 
     # set active partition on disks
@@ -1059,9 +1428,15 @@ class FileSystemSet:
 
         bootDev = dev.device
 
+        if dev.getName() != "RAIDDevice":
+            part = partedUtils.get_partition_by_name(diskset.disks, bootDev)
+            drive = partedUtils.get_partition_drive(part)
+
         for drive in diskset.disks.keys():
             foundActive = 0
             bootPart = None
+            if partedUtils.hasGptLabel(diskset, drive):
+                continue
             disk = diskset.disks[drive]
             part = disk.next_partition()
             while part:
@@ -1093,13 +1468,61 @@ class FileSystemSet:
             if bootPart:
                 del bootPart
 
-    def formatSwap(self, chroot, forceFormat=False):
+    def resizeFilesystems (self, diskset, chroot = '/', shrink = False, grow = False):
+        todo = []
+        for entry in self.entries:
+            if not entry.fsystem or not entry.fsystem.isResizable():
+                continue
+            if entry.fsystem.isFormattable() and entry.getFormat():
+                continue
+            if entry.resizeTargetSize is None:
+                continue
+            if shrink and not (entry.resizeTargetSize < entry.resizeOrigSize):
+                continue
+            if grow and not (entry.resizeTargetSize > entry.resizeOrigSize):
+                continue
+            todo.append(entry)
+        if len(todo) == 0:
+            return
+
+        # we have to have lvm activated to be able to do resizes of LVs
+        lvmActive = lvm.vgcheckactive()
+        devicesActive = diskset.devicesOpen
+
+        if not devicesActive:
+            # should this not be diskset.openDevices() ?
+            diskset.startMPath()
+            diskset.startDmRaid()
+            diskset.startMdRaid()
+
+        if not lvmActive:
+            lvm.vgscan()
+            lvm.vgactivate()
+
+        for entry in todo:
+            entry.fsystem.resize(entry, entry.resizeTargetSize,
+                                 self.progressWindow, chroot)
+        if not lvmActive:
+            lvm.vgdeactivate()
+
+        if not devicesActive:
+            # should this not be diskset.closeDevices() ?
+            diskset.stopMPath()
+            diskset.stopDmRaid()
+            diskset.stopMdRaid()
+
+    def shrinkFilesystems (self, diskset, chroot):
+        self.resizeFilesystems(diskset, chroot, shrink = True)
+    def growFilesystems (self, diskset, chroot):
+        self.resizeFilesystems(diskset, chroot, grow = True)
+
+    def formatSwap (self, chroot, forceFormat=False):
         formatted = []
         notformatted = []
 
         for entry in self.entries:
             if (not entry.fsystem or not entry.fsystem.getName() == "swap" or
-                    entry.isMounted()):
+                entry.isMounted()):
                 continue
             if not entry.getFormat():
                 if not forceFormat:
@@ -1115,7 +1538,7 @@ class FileSystemSet:
                                          "initialize swap on device %s.  This "
                                          "problem is serious, and the install "
                                          "cannot continue.\n\n"
-                                         "Press <Enter> to reboot your system.")
+                                         "Press <Enter> to exit the installer.")
                                        % (entry.device.getDevice(),))
                 sys.exit(0)
 
@@ -1138,12 +1561,12 @@ class FileSystemSet:
             if label:
                 entry.setLabel(label)
 
-    def turnOnSwap(self, chroot, upgrading=False):
+    def turnOnSwap (self, chroot, upgrading=False):
         def swapErrorDialog (msg, format_button_text, entry):
-            buttons = [_("Skip"), format_button_text, _("Reboot")]
+            buttons = [_("Skip"), format_button_text, _("_Exit installer")]
             ret = self.messageWindow(_("Error"), msg, type="custom",
-                                                                                                                    custom_buttons=buttons,
-                                                                                                                    custom_icon="warning")
+                                     custom_buttons=buttons,
+                                     custom_icon="warning")
             if ret == 0:
                 self.entries.remove(entry)
             elif ret == 1:
@@ -1155,7 +1578,7 @@ class FileSystemSet:
 
         for entry in self.entries:
             if (entry.fsystem and entry.fsystem.getName() == "swap"
-                            and not entry.isMounted()):
+                and not entry.isMounted()):
                 try:
                     entry.mount(chroot)
                     self.mountcount = self.mountcount + 1
@@ -1187,50 +1610,45 @@ class FileSystemSet:
                                     "If you are performing a new install, "
                                     "make sure the installer is set "
                                     "to format all swap partitions.") \
-                                   % (entry.device.getDevice())
+                                  % (entry.device.getDevice())
 
                         # choose your own adventure swap partitions...
                         msg = msg + _("\n\nChoose Skip if you want the "
-                                      "installer to ignore this partition during "
-                                      "the upgrade.  Choose Format to reformat "
-                                      "the partition as swap space.  Choose Reboot "
-                                      "to restart the system.")
+                              "installer to ignore this partition during "
+                              "the upgrade.  Choose Format to reformat "
+                              "the partition as swap space.")
 
                         swapErrorDialog(msg, _("Format"), entry)
                     else:
                         sys.exit(0)
-
                 except SystemError, (num, msg):
                     if self.messageWindow:
-                        if upgrading:
-                            self.messageWindow(_("Error"),
-                                               _("Error enabling swap device "
-                                                 "%s: %s\n\n"
-                                                 "The /etc/fstab on your "
-                                                 "upgrade partition does not "
-                                                 "reference a valid swap "
-                                                 "partition.\n\n"
-                                                 "Press OK to reboot your "
-                                                 "system.")
-                                               % (entry.device.getDevice(), msg))
+                        if upgrading and not entry.getLabel():
+                            err = _("Error enabling swap device %s: %s\n\n"
+                                    "Devices in /etc/fstab should be specified "
+                                    "by label, not by device name.\n\nPress "
+                                    "OK to exit the installer.") % (entry.device.getDevice(), msg)
+                        elif upgrading:
+                            err = _("Error enabling swap device %s: %s\n\n"
+                                    "The /etc/fstab on your upgrade partition "
+                                    "does not reference a valid swap "
+                                    "partition.\n\nPress OK to exit the "
+                                    "installer") % (entry.device.getDevice(), msg)
                         else:
-                            self.messageWindow(_("Error"),
-                                               _("Error enabling swap device "
-                                                 "%s: %s\n\n"
-                                                 "This most likely means this "
-                                                 "swap partition has not been "
-                                                 "initialized.\n\n"
-                                                 "Press OK to reboot your "
-                                                 "system.")
-                                               % (entry.device.getDevice(), msg))
+                            err = _("Error enabling swap device %s: %s\n\n"
+                                    "This most likely means this swap "
+                                    "partition has not been initialized.\n\n"
+                                    "Press OK to exit the installer.") % (entry.device.getDevice(), msg)
+
+                    self.messageWindow(_("Error"), err)
                     sys.exit(0)
 
-    def labelEntry(self, entry, chroot):
+    def labelEntry(self, entry, chroot, ignoreExisting = False):
         label = entry.device.getLabel()
-        if label:
+        if label and not ignoreExisting:
             entry.setLabel(label)
-            if labelFactory.isLabelReserved(label):
-                entry.device.doLabel = 1
+            entry.device.doLabel = 1
+
         if entry.device.doLabel is not None:
             entry.fsystem.labelDevice(entry, chroot)
 
@@ -1240,9 +1658,6 @@ class FileSystemSet:
         entry.fsystem.clobberDevice(entry, chroot)
         entry.fsystem.formatDevice(entry, self.progressWindow, chroot)
 
-    def badblocksEntry(self, entry, chroot):
-        entry.fsystem.badblocksDevice(entry, self.progressWindow, chroot)
-
     def getMigratableEntries(self):
         retval = []
         for entry in self.entries:
@@ -1258,44 +1673,36 @@ class FileSystemSet:
                 list.append (entry)
         return list
 
-    def checkBadblocks(self, chroot='/'):
+    def createLogicalVolumes (self, chroot='/'):
+        vgs = {}
+        # first set up the volume groups
         for entry in self.entries:
-            if (not entry.fsystem.isFormattable() or not entry.getBadblocks()
-                            or entry.isMounted()):
-                continue
-            try:
-                self.badblocksEntry(entry, chroot)
-            except BadBlocksError:
-                log.error("Bad blocks detected on device %s",entry.device.getDevice())
-                if self.messageWindow:
-                    self.messageWindow(_("Error"),
-                                       _("Bad blocks have been detected on "
-                                         "device /dev/%s. We do "
-                                         "not recommend you use this device."
-                                         "\n\n"
-                                         "Press <Enter> to reboot your system")
-                                       % (entry.device.getDevice(),))
-                sys.exit(0)
+            if entry.fsystem.name == "volume group (LVM)":
+                entry.device.setupDevice(chroot)
+                vgs[entry.device.name] = entry.device
 
-            except SystemError:
-                if self.messageWindow:
-                    self.messageWindow(_("Error"),
-                                       _("An error occurred searching for "
-                                         "bad blocks on %s.  This problem is "
-                                         "serious, and the install cannot "
-                                         "continue.\n\n"
-                                         "Press <Enter> to reboot your system.")
-                                       % (entry.device.getDevice(),))
-                sys.exit(0)
+        # then set up the logical volumes
+        for entry in self.entries:
+            if isinstance(entry.device, LogicalVolumeDevice):
+                vg = None
+                if vgs.has_key(entry.device.vgname):
+                    vg = vgs[entry.device.vgname]
+                entry.device.setupDevice(chroot, vgdevice = vg)
+        self.volumesCreated = 1
 
-    def makeFilesystems(self, chroot='/'):
+
+    def makeFilesystems (self, chroot='/', skiprootfs=False):
         formatted = []
         notformatted = []
         for entry in self.entries:
             if (not entry.fsystem.isFormattable() or not entry.getFormat()
-                            or entry.isMounted()):
+                or entry.isMounted()):
                 notformatted.append(entry)
                 continue
+            # FIXME: this is a bit of a hack, but works
+            if (skiprootfs and entry.mountpoint == '/'):
+                formatted.append(entry)
+                continue
             try:
                 self.formatEntry(entry, chroot)
                 formatted.append(entry)
@@ -1306,8 +1713,8 @@ class FileSystemSet:
                                          "format %s.  This problem is "
                                          "serious, and the install cannot "
                                          "continue.\n\n"
-                                         "Press <Enter> to reboot your system.")
-                                    % (entry.device.getDevice(),))
+                                         "Press <Enter> to exit the installer.")
+                                       % (entry.device.getDevice(),))
                 sys.exit(0)
 
         for entry in formatted:
@@ -1336,7 +1743,7 @@ class FileSystemSet:
     def haveMigratedFilesystems(self):
         return self.migratedfs
 
-    def migrateFilesystems(self, chroot='/'):
+    def migrateFilesystems (self, pomona):
         if self.migratedfs:
             return
 
@@ -1347,7 +1754,8 @@ class FileSystemSet:
             if not entry.origfsystem.isMigratable() or not entry.getMigrate():
                 continue
             try:
-                entry.origfsystem.migrateFileSystem(entry, self.messageWindow, chroot)
+                entry.origfsystem.migrateFileSystem(entry, self.messageWindow,
+                                                    pomona.rootPath)
             except SystemError:
                 if self.messageWindow:
                     self.messageWindow(_("Error"),
@@ -1355,24 +1763,33 @@ class FileSystemSet:
                                          "migrate %s.  This problem is "
                                          "serious, and the install cannot "
                                          "continue.\n\n"
-                                         "Press <Enter> to reboot your system.")
+                                         "Press <Enter> to exit the installer.")
                                        % (entry.device.getDevice(),))
                 sys.exit(0)
 
-            self.migratedfs = 1
+        # we need to unmount and remount so that we're mounted as the
+        # new fstype as we want to use the new filesystem type during
+        # the upgrade for ext3->ext4 migrations
+        if self.isActive():
+            self.umountFilesystems(pomona.rootPath, swapoff = False)
+            self.mountFilesystems(pomona)
+
+        self.migratedfs = 1
 
-    def mountFilesystems(self, pomona, raiseErrors = 0, readOnly = 0):
-        #protected = pomona.method.protectedPartitions()
-        protected = []
+    def mountFilesystems(self, pomona, raiseErrors = 0, readOnly = 0, skiprootfs = 0):
+        protected = pomona.id.partitions.protectedPartitions()
 
         for entry in self.entries:
             # Don't try to mount a protected partition, since it will already
             # have been mounted as the installation source.
-            if not entry.fsystem.isMountable() or (protected and entry.device.getDevice() in protected):
+            if protected and entry.device.getDevice() in protected and os.path.ismount("/mnt/isodir"):
+                continue
+
+            if not entry.fsystem.isMountable() or (skiprootfs and entry.mountpoint == '/'):
                 continue
 
             try:
-                log.info("trying to mount %s on %s" %(entry.device.getDevice(), entry.mountpoint))
+                log.info("trying to mount %s on %s" %(entry.device.setupDevice(), entry.mountpoint,))
                 entry.mount(pomona.rootPath, readOnly = readOnly)
                 self.mountcount = self.mountcount + 1
             except OSError, (num, msg):
@@ -1384,56 +1801,111 @@ class FileSystemSet:
                                              "this path is not a directory. "
                                              "This is a fatal error and the "
                                              "install cannot continue.\n\n"
-                                             "Press <Enter> to reboot your "
-                                             "system.") % (entry.mountpoint,))
+                                             "Press <Enter> to exit the "
+                                             "installer.") % (entry.mountpoint,))
                     else:
                         self.messageWindow(_("Invalid mount point"),
                                            _("An error occurred when trying "
                                              "to create %s: %s.  This is "
                                              "a fatal error and the install "
                                              "cannot continue.\n\n"
-                                             "Press <Enter> to reboot your "
-                                             "system.") % (entry.mountpoint, msg))
-                    sys.exit(0)
+                                             "Press <Enter> to exit the "
+                                             "installer.") % (entry.mountpoint,
+                                                           msg))
+                log.error("OSError: (%d) %s" % (num, msg) )
+                sys.exit(0)
             except SystemError, (num, msg):
                 if raiseErrors:
                     raise SystemError, (num, msg)
-
                 if self.messageWindow:
                     if not entry.fsystem.isLinuxNativeFS():
                         ret = self.messageWindow(_("Unable to mount filesystem"),
                                                  _("An error occurred mounting "
-                                                   "device %s as %s.  You may "
-                                                   "continue installation, but "
-                                                   "there may be problems.")
-                                                 % (entry.device.getDevice(),
-                                                    entry.mountpoint),
-                                                    type="custom", custom_icon="warning",
-                                                    custom_buttons=[_("_Reboot"), _("_Continue")])
+                                                 "device %s as %s.  You may "
+                                                 "continue installation, but "
+                                                 "there may be problems.") %
+                                                 (entry.device.getDevice(),
+                                                  entry.mountpoint),
+                                                 type="custom", custom_icon="warning",
+                                                 custom_buttons=[_("_Exit installer"),
+                                                                _("_Continue")])
 
                         if ret == 0:
                             sys.exit(0)
                         else:
                             continue
                     else:
-                        if pomona.id.getUpgrade() and not entry.getLabel():
+                        if pomona.id.getUpgrade() and not (entry.getLabel() or entry.getUuid()):
                             errStr = _("Error mounting device %s as %s: "
                                        "%s\n\n"
-                                       "Devices in /etc/fstab should be "
-                                       "specified by label, not by device name."
+                                       "Devices in /etc/fstab should be specified "
+                                       "by label or UUID, not by device name."
                                        "\n\n"
-                                       "Press OK to reboot your system.") % (entry.device.getDevice(), entry.mountpoint, msg)
+                                       "Press OK to exit the installer.") % (entry.device.getDevice(), entry.mountpoint, msg)
                         else:
                             errStr = _("Error mounting device %s as %s: "
                                        "%s\n\n"
-                                       "This most likely means this "
-                                       "partition has not been formatted."
-                                       "\n\n"
-                                       "Press OK to reboot your system.") % (entry.device.getDevice(), entry.mountpoint, msg)
+                                       "Press OK to exit the installer.") % (entry.device.getDevice(), entry.mountpoint, msg)
 
                         self.messageWindow(_("Error"), errStr)
 
-                        sys.exit(0)
+                log.error("SystemError: (%d) %s" % (num, msg) )
+                sys.exit(0)
+
+        self.makeLVMNodes(pomona.rootPath)
+
+    def makeLVMNodes(self, instPath, trylvm1 = 0):
+        # XXX hack to make the device node exist for the root fs if
+        # it's a logical volume so that mkinitrd can create the initrd.
+        root = self.getEntryByMountPoint("/")
+        if not root:
+            if self.messageWindow:
+                self.messageWindow(_("Error"),
+                                   _("Error finding / entry.\n\n"
+                                   "This is most likely means that "
+                                   "your fstab is incorrect."
+                                   "\n\n"
+                                   "Press OK to exit the installer."))
+            sys.exit(0)
+
+        rootlvm1 = 0
+        if trylvm1:
+            dev = root.device.getDevice()
+            # lvm1 major is 58
+            if os.access("%s/dev/%s" %(instPath, dev), os.R_OK) and posix.major(os.stat("%s/dev/%s" %(instPath, dev)).st_rdev) == 58:
+                rootlvm1 = 1
+
+        if isinstance(root.device, LogicalVolumeDevice) or rootlvm1:
+            # now make sure all of the device nodes exist.  *sigh*
+            rc = lvm.vgmknodes()
+
+            rootDev = "/dev/%s" % (root.device.getDevice(),)
+            rootdir = instPath + os.path.dirname(rootDev)
+            if not os.path.isdir(rootdir):
+                os.makedirs(rootdir)
+
+            if root.device.crypto is None:
+                dmdev = "/dev/mapper/" + root.device.getDevice().replace("-","--").replace("/", "-")
+            else:
+                dmdev = "/dev/" + root.device.getDevice()
+
+            if os.path.exists(instPath + dmdev):
+                os.unlink(instPath + dmdev)
+            if not os.path.isdir(os.path.dirname(instPath + dmdev)):
+                os.makedirs(os.path.dirname(instPath + dmdev))
+            iutil.copyDeviceNode(dmdev, instPath + dmdev)
+
+            # unlink existing so that we dtrt on upgrades
+            if os.path.exists(instPath + rootDev) and not root.device.crypto:
+                os.unlink(instPath + rootDev)
+            if not os.path.isdir(rootdir):
+                os.makedirs(rootdir)
+
+            if root.device.crypto is None:
+                os.symlink(dmdev, instPath + rootDev)
+
+            if not os.path.isdir("%s/etc/lvm" %(instPath,)):
+                os.makedirs("%s/etc/lvm" %(instPath,))
 
     def filesystemSpace(self, chroot='/'):
         space = []
@@ -1457,6 +1929,7 @@ class FileSystemSet:
                 return -1
             elif s1 < s2:
                 return 1
+
             return 0
 
         space.sort(spaceSort)
@@ -1481,13 +1954,10 @@ class FileSystemSet:
         return ret
 
     def umountFilesystems(self, instPath, ignoreErrors = 0, swapoff = True):
-        # XXX remove special case
-        try:
-            isys.umount(instPath + '/proc/bus/usb', removeDir = 0)
-            log.info("Umount USB OK")
-        except:
-#           log.error("Umount USB Fail")
-            pass
+        # Unmount things bind mounted into the instPath here because they're
+        # not tracked by self.entries.
+        if os.path.ismount("%s/dev" % instPath):
+            isys.umount("%s/dev" % instPath, removeDir=0)
 
         # take a slice so we don't modify self.entries
         reverse = self.entries[:]
@@ -1497,13 +1967,14 @@ class FileSystemSet:
             if entry.mountpoint == "swap" and not swapoff:
                 continue
             entry.umount(instPath)
+            entry.device.cleanupDevice(instPath)
 
 class FileSystemSetEntry:
     def __init__ (self, device, mountpoint,
                   fsystem=None, options=None,
                   origfsystem=None, migrate=0,
                   order=-1, fsck=-1, format=0,
-                  badblocks = 0, bytesPerInode=4096, fsprofile=None):
+                  fsprofile=None):
         if not fsystem:
             fsystem = fileSystemTypeGet("ext2")
         self.device = device
@@ -1511,11 +1982,9 @@ class FileSystemSetEntry:
         self.fsystem = fsystem
         self.origfsystem = origfsystem
         self.migrate = migrate
-        if options:
-            self.options = options
-        else:
-            self.options = fsystem.getDefaultOptions(mountpoint)
-        self.options += device.getDeviceOptions()
+        self.resizeTargetSize = None
+        self.resizeOrigSize = None
+        self.options = options
         self.mountcount = 0
         self.label = None
         if fsck == -1:
@@ -1531,39 +2000,29 @@ class FileSystemSetEntry:
                 self.order = 0
         else:
             self.order = order
-            if format and not fsystem.isFormattable():
-                raise RuntimeError, ("file system type %s is not formattable, "
-                                     "but has been added to fsset with format "
-                                     "flag on" % fsystem.getName())
+        if format and not fsystem.isFormattable():
+            raise RuntimeError, ("file system type %s is not formattable, "
+                                 "but has been added to fsset with format "
+                                 "flag on" % fsystem.getName())
         self.format = format
-        self.badblocks = badblocks
-        self.bytesPerInode = bytesPerInode
         self.fsprofile = fsprofile
 
     def mount(self, chroot='/', devPrefix='/dev', readOnly = 0):
         device = self.device.setupDevice(chroot, devPrefix=devPrefix)
 
-        # FIXME: we really should migrate before turnOnFilesystems.
-        # but it's too late now
-        if (self.migrate == 1) and (self.origfsystem is not None):
-            self.origfsystem.mount(device, "%s" % (self.mountpoint,),
-                                   readOnly = readOnly,
-                                   bindMount = isinstance(self.device,
-                                   BindMountDevice),
-                                   instroot = chroot)
-        else:
-            self.fsystem.mount(device, "%s" % (self.mountpoint,),
-                               readOnly = readOnly,
-                               bindMount = isinstance(self.device,
-                               BindMountDevice),
-                               instroot = chroot)
+        self.fsystem.mount(device, "%s" % (self.mountpoint,),
+                           readOnly = readOnly,
+                           bindMount = isinstance(self.device,
+                                                  BindMountDevice),
+                           instroot = chroot)
 
         self.mountcount = self.mountcount + 1
 
     def umount(self, chroot='/'):
         if self.mountcount > 0:
             try:
-                self.fsystem.umount(self.device, "%s/%s" % (chroot, self.mountpoint))
+                self.fsystem.umount(self.device, "%s/%s" % (chroot,
+                                                            self.mountpoint))
                 self.mountcount = self.mountcount - 1
             except RuntimeError:
                 pass
@@ -1571,12 +2030,6 @@ class FileSystemSetEntry:
     def setFileSystemType(self, fstype):
         self.fsystem = fstype
 
-    def setBadblocks(self, state):
-        self.badblocks = state
-
-    def getBadblocks(self):
-        return self.badblocks
-
     def getMountPoint(self):
         return self.mountpoint
 
@@ -1597,11 +2050,21 @@ class FileSystemSetEntry:
     def setMigrate (self, state):
         if self.format and state:
             raise ValueError, "Trying to set migrate bit on when format is set!"
+
         self.migrate = state
 
     def getMigrate (self):
         return self.migrate
 
+    def setResizeTarget (self, targetsize, size):
+        if not self.fsystem.isResizable() and targetsize is not None:
+            raise ValueError, "Can't set a resize target for a non-resizable filesystem"
+        self.resizeTargetSize = targetsize
+        self.resizeOrigSize = size
+
+    def getResizeTarget (self):
+        return self.resizeTargetSize
+
     def isMounted (self):
         return self.mountcount > 0
 
@@ -1632,24 +2095,36 @@ class FileSystemSetEntry:
 
 
 class Device:
-    def __init__(self, device = "none"):
+    def __init__(self, device = "none", encryption=None):
         self.device = device
         self.label = None
         self.isSetup = 0
         self.doLabel = 1
         self.deviceOptions = ""
+        if encryption:
+            self.crypto = encryption
+            # mount by device since the name is based only on UUID
+            self.doLabel = None
+            if device not in ("none", None):
+                self.crypto.setDevice(device)
+        else:
+            self.crypto = None
 
     def getComment (self):
         return ""
 
     def getDevice (self, asBoot = 0):
-        return self.device
+        if self.crypto:
+            return self.crypto.getDevice()
+        else:
+            return self.device
 
-    def setupDevice (self, chroot='/', devPrefix='/dev'):
+    def setupDevice (self, chroot='/', devPrefix='/dev/'):
         return self.device
 
-    def cleanupDevice (self, chroot, devPrefix='/dev'):
-        pass
+    def cleanupDevice (self, chroot, devPrefix='/dev/'):
+        if self.crypto:
+            self.crypto.closeDevice()
 
     def solidify (self):
         pass
@@ -1678,30 +2153,295 @@ class Device:
         return self.deviceOptions
 
 class DevDevice(Device):
-    """ Device with a device node rooted in /dev that we just always use
-        the pre-created device node for."""
+    """Device with a device node rooted in /dev that we just always use
+       the pre-created device node for."""
     def __init__(self, dev):
-        Device.__init__(self)
-        self.device = dev
+        Device.__init__(self, device=dev)
 
     def getDevice(self, asBoot = 0):
         return self.device
 
     def setupDevice(self, chroot='/', devPrefix='/dev'):
-        return "/dev/%s" %(self.getDevice(),)
+        #We use precreated device but we have to make sure that the device exists
+        path = '/dev/%s' % (self.getDevice(),)
+        return path
 
+class RAIDDevice(Device):
+    # XXX usedMajors does not take in account any EXISTING md device
+    #     on the system for installs.  We need to examine all partitions
+    #     to investigate which minors are really available.
+    usedMajors = {}
+
+    # members is a list of Device based instances that will be
+    # a part of this raid device
+    def __init__(self, level, members, minor=-1, spares=0, existing=0,
+                 chunksize = 64, encryption=None):
+        Device.__init__(self, encryption=encryption)
+        self.level = level
+        self.members = members
+        self.spares = spares
+        self.numDisks = len(members) - spares
+        self.isSetup = existing
+        self.doLabel = None
+        if chunksize is not None:
+            self.chunksize = chunksize
+        else:
+            self.chunksize = 256
+
+        if len(members) < spares:
+            raise RuntimeError, ("you requested more spare devices "
+                                 "than online devices!")
+
+        if level == 5:
+            if self.numDisks < 3:
+                raise RuntimeError, "RAID 5 requires at least 3 online members"
+
+        # there are 32 major md devices, 0...31
+        if minor == -1 or minor is None:
+            for I in range(32):
+                if not RAIDDevice.usedMajors.has_key(I):
+                    minor = I
+                    break
+
+            if minor == -1:
+                raise RuntimeError, ("Unable to allocate minor number for "
+                                     "raid device")
+
+        RAIDDevice.usedMajors[minor] = None
+        self.device = "md" + str(minor)
+        self.minor = minor
+
+        if self.crypto:
+            self.crypto.setDevice(self.device)
+
+        # make sure the list of raid members is sorted
+        self.members.sort(cmp=lambda x,y: cmp(x.getDevice(),y.getDevice()))
+
+    def __del__ (self):
+        del RAIDDevice.usedMajors[self.minor]
+
+    def ext2Args (self):
+        if self.level == 5:
+            return [ '-R', 'stride=%d' % ((self.numDisks - 1) * 16) ]
+        elif self.level == 0:
+            return [ '-R', 'stride=%d' % (self.numDisks * 16) ]
+        return []
+
+    def mdadmLine (self, devPrefix="/dev"):
+        levels = { 0: "raid0",
+                   1: "raid1",
+                   4: "raid5",
+                   5: "raid5",
+                   6: "raid6",
+                  10: "raid10" }
+
+        # If we can't find the device for some reason, revert to old behavior.
+        try:
+            (dev, devices, level, numActive) = raid.lookup_raid_device (self.device)
+        except KeyError:
+            devices = []
+
+        # First loop over all the devices that make up the RAID trying to read
+        # the superblock off each.  If we read a superblock, return a line that
+        # can go into the mdadm.conf.  If we fail, fall back to the old method
+        # of using the super-minor.
+        for d in devices:
+            try:
+                (major, minor, uuid, level, nrDisks, totalDisks, mdMinor) = \
+                    isys.raidsb(d)
+                return "ARRAY %s/%s level=%s num-devices=%d uuid=%s\n" \
+                    %(devPrefix, self.device, levels[level], nrDisks, uuid)
+            except ValueError:
+                pass
+
+        return "ARRAY %s/%s super-minor=%s\n" %(devPrefix, self.device,
+                                                self.minor)
+
+    def raidTab (self, devPrefix='/dev'):
+        entry = ""
+        entry = entry + "raiddev                    %s/%s\n" % (devPrefix,
+                                                                self.device,)
+        entry = entry + "raid-level                 %d\n" % (self.level,)
+        entry = entry + "nr-raid-disks              %d\n" % (self.numDisks,)
+        entry = entry + "chunk-size                 %s\n" %(self.chunksize,)
+        entry = entry + "persistent-superblock      1\n"
+        entry = entry + "nr-spare-disks             %d\n" % (self.spares,)
+        i = 0
+        for device in [m.getDevice() for m in self.members[:self.numDisks]]:
+            entry = entry + "    device     %s/%s\n" % (devPrefix,
+                                                        device)
+            entry = entry + "    raid-disk     %d\n" % (i,)
+            i = i + 1
+        i = 0
+        for device in [m.getDevice() for m in self.members[self.numDisks:]]:
+            entry = entry + "    device     %s/%s\n" % (devPrefix,
+                                                        device)
+            entry = entry + "    spare-disk     %d\n" % (i,)
+            i = i + 1
+        return entry
+
+    def setupDevice (self, chroot="/", devPrefix='/dev'):
+        if not self.isSetup:
+            memberDevs = []
+            for pd in self.members:
+                memberDevs.append(pd.setupDevice(chroot, devPrefix=devPrefix))
+                if pd.isNetdev(): self.setAsNetdev()
+
+            args = ["--create", "/dev/%s" %(self.device,),
+                    "--run", "--chunk=%s" %(self.chunksize,),
+                    "--level=%s" %(self.level,),
+                    "--raid-devices=%s" %(self.numDisks,)]
+
+            if self.spares > 0:
+                args.append("--spare-devices=%s" %(self.spares,),)
+
+            args.extend(memberDevs)
+            log.info("going to run: %s" %(["mdadm"] + args,))
+            iutil.execWithRedirect ("mdadm", args,
+                                    stderr="/dev/tty5", stdout="/dev/tty5",
+                                    searchPath = 1)
+            raid.register_raid_device(self.device,
+                                      [m.getDevice() for m in self.members],
+                                      self.level, self.numDisks)
+            self.isSetup = 1
+        else:
+            isys.raidstart(self.device, self.members[0].getDevice())
+
+        if self.crypto:
+            self.crypto.formatDevice()
+            self.crypto.openDevice()
+            node = "%s/%s" % (devPrefix, self.crypto.getDevice())
+        else:
+            node = "%s/%s" % (devPrefix, self.device)
+
+        return node
+
+    def getDevice (self, asBoot = 0):
+        if not asBoot and self.crypto:
+            return self.crypto.getDevice()
+        elif not asBoot:
+            return self.device
+        else:
+            return self.members[0].getDevice(asBoot=asBoot)
+
+    def solidify(self):
+        return
 
 ext2 = fileSystemTypeGet("ext2")
+ext2.registerDeviceArgumentFunction(RAIDDevice, RAIDDevice.ext2Args)
+
+class VolumeGroupDevice(Device):
+    def __init__(self, name, physvols, pesize = 32768, existing = 0):
+        """Creates a VolumeGroupDevice.
+
+        name is the name of the volume group
+        physvols is a list of Device objects which are the physical volumes
+        pesize is the size of physical extents in kilobytes
+        existing is whether this vg previously existed.
+        """
 
-class PartitionDevice(Device):
-    def __init__(self, partition):
         Device.__init__(self)
+        self.physicalVolumes = physvols
+        self.isSetup = existing
+        self.name = name
+        self.device = name
+        self.isSetup = existing
+
+        self.physicalextentsize = pesize
+
+    def setupDevice (self, chroot="/", devPrefix='/dev/'):
+        nodes = []
+        for volume in self.physicalVolumes:
+            # XXX the lvm tools are broken and will only work for /dev
+            node = volume.setupDevice(chroot, devPrefix="/dev")
+            if volume.isNetdev(): self.setAsNetdev()
+
+            # XXX I should check if the pv is set up somehow so that we
+            # can have preexisting vgs and add new pvs to them.
+            if not self.isSetup:
+                lvm.pvcreate(node)
+                nodes.append(node)
+
+        if not self.isSetup:
+            lvm.vgcreate(self.name, self.physicalextentsize, nodes)
+            self.isSetup = 1
+        else:
+            lvm.vgscan()
+            lvm.vgactivate()
+
+        return "/dev/%s" % (self.name,)
+
+    def solidify(self):
+        return
+
+class LogicalVolumeDevice(Device):
+    # note that size is in megabytes!
+    def __init__(self, vgname, size, lvname, vg, existing = 0, encryption=None):
+        Device.__init__(self, encryption=encryption)
+        self.vgname = vgname
+        self.size = size
+        self.name = lvname
+        self.isSetup = 0
+        self.isSetup = existing
+        self.doLabel = None
+        self.vg = vg
+
+        # these are attributes we might want to expose.  or maybe not.
+        # self.chunksize
+        # self.stripes
+        # self.stripesize
+        # self.extents
+        # self.readaheadsectors
+
+    def setupDevice(self, chroot="/", devPrefix='/dev', vgdevice = None):
+        if self.crypto:
+            self.crypto.setDevice("mapper/%s-%s" % (self.vgname, self.name))
+
+        if not self.isSetup:
+            lvm.lvcreate(self.name, self.vgname, self.size)
+            self.isSetup = 1
+
+            if vgdevice and vgdevice.isNetdev():
+                self.setAsNetdev()
+
+        if self.crypto:
+            self.crypto.formatDevice()
+            self.crypto.openDevice()
+
+        return "/dev/%s" % (self.getDevice(),)
+
+    def getDevice(self, asBoot = 0):
+        if self.crypto and not asBoot:
+            device = self.crypto.getDevice()
+        else:
+            device = "%s/%s" % (self.vgname, self.name)
+
+        return device
+
+    def solidify(self):
+        return
+
+
+class PartitionDevice(Device):
+    def __init__(self, partition, encryption=None):
         if type(partition) != types.StringType:
             raise ValueError, "partition must be a string"
-        self.device = partition
+        Device.__init__(self, device=partition, encryption=encryption)
+
+        (disk, pnum) = getDiskPart(partition)
+
+    def getDevice(self, asBoot = 0):
+        if self.crypto and not asBoot:
+            return self.crypto.getDevice()
+        else:
+            return self.device
 
     def setupDevice(self, chroot="/", devPrefix='/dev'):
-        path = '%s/%s' % (devPrefix, self.getDevice(),)
+        path = '%s/%s' % (devPrefix, self.device)
+        if self.crypto:
+            self.crypto.formatDevice()
+            self.crypto.openDevice()
+            path = "%s/%s" % (devPrefix, self.crypto.getDevice())
         return path
 
 class PartedPartitionDevice(PartitionDevice):
@@ -1739,7 +2479,7 @@ class SwapFileDevice(Device):
     def setSize (self, size):
         self.size = size
 
-    def setupDevice (self, chroot="/", devPrefix='/tmp'):
+    def setupDevice (self, chroot="/", devPrefix='/dev'):
         file = os.path.normpath(chroot + self.getDevice())
         if not os.access(file, os.R_OK):
             if self.size:
@@ -1749,7 +2489,7 @@ class SwapFileDevice(Device):
                 isys.ddfile(file, self.size, None)
             else:
                 raise SystemError, (0, "swap file creation necessary, but "
-                                       "required size is unknown.")
+                                    "required size is unknown.")
         return file
 
 # This is a device that describes a swap file that is sitting on
@@ -1761,7 +2501,7 @@ class PiggybackSwapFileDevice(SwapFileDevice):
         SwapFileDevice.__init__(self, file)
         self.piggypath = piggypath
 
-    def setupDevice(self, chroot="/", devPrefix='/tmp'):
+    def setupDevice(self, chroot="/", devPrefix='/dev'):
         return SwapFileDevice.setupDevice(self, self.piggypath, devPrefix)
 
 class LoopbackDevice(Device):
@@ -1771,7 +2511,7 @@ class LoopbackDevice(Device):
         self.hostfs = hostFs
         self.device = "loop1"
 
-    def setupDevice(self, chroot="/", devPrefix='/tmp/'):
+    def setupDevice(self, chroot="/", devPrefix='/dev/'):
         if not self.isSetup:
             isys.mount(self.host[5:], "/mnt/loophost", fstype = "vfat")
             self.device = allocateLoopback("/mnt/loophost/redhat.img")
@@ -1781,7 +2521,6 @@ class LoopbackDevice(Device):
             path = '%s/%s' % (devPrefix, self.getDevice())
         else:
             path = '%s/%s' % (devPrefix, self.getDevice())
-            #isys.makeDevInode(self.getDevice(), path)
         path = os.path.normpath(path)
         return path
 
@@ -1789,10 +2528,62 @@ class LoopbackDevice(Device):
         return "# LOOP1: %s %s /redhat.img\n" % (self.host, self.hostfs)
 
 def makeDevice(dev):
-    device = DevDevice(dev)
+    cryptoDev = partitions.lookup_cryptodev(dev)
+    if cryptoDev and cryptoDev.getDevice() == dev:
+        dev = cryptoDev.getDevice(encrypted=True)
+
+    if dev.startswith('md'):
+        try:
+            (mdname, devices, level, numActive) = raid.lookup_raid_device(dev)
+            # convert devices to Device instances and sort out encryption
+            devList = []
+            for dev in devices:
+                cryptoMem = partitions.lookup_cryptodev(dev)
+                if cryptoMem and cryptoMem.getDevice() == dev:
+                    dev = cryptoMem.getDevice(encrypted=True)
+
+                devList.append(PartitionDevice(dev, encryption=cryptoMem))
+
+            device = RAIDDevice(level, devList,
+                                minor=int(mdname[2:]),
+                                spares=len(devices) - numActive,
+                                existing=1, encryption=cryptoDev)
+        except KeyError:
+            device = PartitionDevice(dev, encryption=cryptoDev)
+    else:
+        device = PartitionDevice(dev, encryption=cryptoDev)
     return device
 
+# XXX fix RAID
 def readFstab(pomona):
+    def createMapping(dict):
+        mapping = {}
+        dupes = []
+
+        for device, info in dict.items():
+            if not mapping.has_key(info):
+                mapping[info] = device
+            elif not info in dupes:
+                dupes.append(info)
+
+        return (mapping, dupes)
+
+    def showError(label, intf):
+        if intf:
+            intf.messageWindow(_("Duplicate Labels"),
+                               _("Multiple devices on your system are "
+                                 "labelled %s.  Labels across devices must be "
+                                 "unique for your system to function "
+                                 "properly.\n\n"
+                                 "Please fix this problem and restart the "
+                                 "installation process.") %(label,),
+                               type="custom", custom_icon="error",
+                               custom_buttons=[_("_Exit installer")])
+            sys.exit(0)
+        else:
+            log.warning("Duplicate labels for %s, but no intf so trying "
+                        "to continue" %(label,))
+
     path = pomona.rootPath + '/etc/fstab'
     intf = pomona.intf
     fsset = FileSystemSet()
@@ -1802,39 +2593,11 @@ def readFstab(pomona):
     # temporary, to get the labels
     diskset = partedUtils.DiskSet(pomona)
     diskset.openDevices()
-    labels = diskset.getLabels()
+    labels = diskset.getInfo()
+    uuids = diskset.getInfo(readFn=lambda d: isys.readFSUuid(d))
 
-    labelToDevice = {}
-    for device, label in labels.items():
-        if not labelToDevice.has_key(label):
-            labelToDevice[label] = device
-        elif intf is not None:
-            try:
-                intf.messageWindow(_("Duplicate Labels"),
-                                   _("Multiple devices on your system are "
-                                     "labelled %s.  Labels across devices must be "
-                                     "unique for your system to function "
-                                     "properly.\n\n"
-                                     "Please fix this problem and restart the "
-                                     "installation process.")
-                                   % (label,), type="custom", custom_icon="error",
-                                     custom_buttons=[_("_Reboot")])
-            except TypeError:
-                intf.messageWindow(_("Invalid Label"),
-                                   _("An invalid label was found on device "
-                                     "%s.  Please fix this problem and restart "
-                                     "the installation process.")
-                                   % (device,), type="custom", custom_icon="error",
-                                     custom_buttons=[_("_Reboot")])
-
-                sys.exit(0)
-        else:
-            log.warning("Duplicate labels for %s, but no intf so trying "
-                        "to continue" % (label,))
-
-    # mark these labels found on the system as used so the factory
-    # doesn't give them to another device
-    labelFactory.reserveLabels(labels)
+    (labelToDevice, labelDupes) = createMapping(labels)
+    (uuidToDevice, uuidDupes) = createMapping(uuids)
 
     loopIndex = {}
 
@@ -1843,7 +2606,7 @@ def readFstab(pomona):
     f.close()
 
     for line in lines:
-        fields = string.split(line)
+        fields = string.split (line)
 
         if not fields: continue
 
@@ -1862,7 +2625,6 @@ def readFstab(pomona):
             fields.append(0)
         elif len(fields) > 6:
             continue
-
         if string.find(fields[3], "noauto") != -1: continue
 
         # shenanigans to handle ext3,ext2 format in fstab
@@ -1881,15 +2643,20 @@ def readFstab(pomona):
         # "none" is valid as an fs type for bind mounts (#151458)
         if fsystem is None and (string.find(fields[3], "bind") == -1):
             continue
+
         label = None
         if fields[0] == "none":
             device = Device()
-        elif ((string.find(fields[3], "bind") != -1) and fields[0].startswith("/")):
+        elif ((string.find(fields[3], "bind") != -1) and
+              fields[0].startswith("/")):
             # it's a bind mount, they're Weird (tm)
             device = BindMountDevice(fields[0])
             fsystem = fileSystemTypeGet("bind")
         elif len(fields) >= 6 and fields[0].startswith('LABEL='):
             label = fields[0][6:]
+            if label in labelDupes:
+                showError(label, intf)
+
             if labelToDevice.has_key(label):
                 device = makeDevice(labelToDevice[label])
             else:
@@ -1897,9 +2664,22 @@ def readFstab(pomona):
                              "could not be found on any file system", label)
                 # bad luck, skip this entry.
                 continue
+        elif len(fields) >= 6 and fields[0].startswith('UUID='):
+            uuid = fields[0][5:]
+            if uuid in uuidDupes:
+                showError(uuid, intf)
+
+            if uuidToDevice.has_key(uuid):
+                device = makeDevice(uuidToDevice[uuid])
+            else:
+                log.warning ("fstab file has UUID=%s, but this UUID"
+                             "could not be found on any file system", uuid)
+                # bad luck, skip this entry.
+                continue
         elif fields[2] == "swap" and not fields[0].startswith('/dev/'):
             # swap files
             file = fields[0]
+
             if file.startswith('/initrd/loopfs/'):
                 file = file[14:]
                 device = PiggybackSwapFileDevice("/mnt/loophost", file)
@@ -1913,7 +2693,12 @@ def readFstab(pomona):
                 (dev, fs) = loopIndex[device]
                 device = LoopbackDevice(dev, fs)
         elif fields[0].startswith('/dev/'):
-            device = makeDevice(fields[0][5:])
+            # Older installs may have lines starting with things like /dev/proc
+            # so watch out for that on upgrade.
+            if fsystem is not None and isinstance(fsystem, PseudoFileSystem):
+                device = Device(device = fields[0][5:])
+            else:
+                device = makeDevice(fields[0][5:])
         else:
             device = Device(device = fields[0])
 
@@ -1933,107 +2718,11 @@ def readFstab(pomona):
         if label:
             entry.setLabel(label)
         fsset.add(entry)
-        return fsset
-
-def getDevFD(device):
-    try:
-        fd = os.open(device, os.O_RDONLY)
-    except:
-        file = '/dev/' + device
-        try:
-            fd = os.open(file, os.O_RDONLY)
-        except:
-            return -1
-    return fd
-
-def isValidExt2(device):
-    fd = getDevFD(device)
-    if fd == -1:
-        return 0
-
-    buf = os.read(fd, 2048)
-    os.close(fd)
-
-    if len(buf) != 2048:
-        return 0
-
-    if struct.unpack("<H", buf[1080:1082]) == (0xef53,):
-        return 1
-
-    return 0
-
-def isValidXFS(device):
-    fd = getDevFD(device)
-    if fd == -1:
-        return 0
-
-    buf = os.read(fd, 4)
-    os.close(fd)
-
-    if len(buf) != 4:
-        return 0
-
-    if buf == "XFSB":
-        return 1
-
-    return 0
-
-def isValidReiserFS(device):
-    fd = getDevFD(device)
-    if fd == -1:
-        return 0
-
-    '''
-    ** reiserfs 3.5.x super block begins at offset 8K
-    ** reiserfs 3.6.x super block begins at offset 64K
-    All versions have a magic value of "ReIsEr" at
-    offset 0x34 from start of super block
-    '''
-    reiserMagicVal = "ReIsEr"
-    reiserMagicOffset = 0x34
-    reiserSBStart = [64*1024, 8*1024]
-    bufSize = 0x40  # just large enough to include the magic value
-    for SBOffset in reiserSBStart:
-        try:
-            os.lseek(fd, SBOffset, 0)
-            buf = os.read(fd, bufSize)
-        except:
-            buf = ""
-
-        if len(buf) < bufSize:
-            continue
-
-        if (buf[reiserMagicOffset:reiserMagicOffset+len(reiserMagicVal)] == reiserMagicVal):
-            os.close(fd)
-            return 1
-
-    os.close(fd)
-    return 0
-
-# this will return a list of types of filesystems which device
-# looks like it could be to try mounting as
-def getFStoTry(device):
-    rc = []
-
-    if isValidXFS(device):
-        rc.append("xfs")
-
-    if isValidReiserFS(device):
-        rc.append("reiserfs")
-
-    if isValidExt2(device):
-        if isys.ext2HasJournal(device):
-            rc.append("ext3")
-        rc.append("ext2")
-
-    ### XXX FIXME: need to check for swap
-
-    return rc
+    return fsset
 
 def allocateLoopback(file):
     found = 1
     for i in range(8):
-        dev = "loop%d" % (i,)
         path = "/dev/loop%d" % (i,)
         try:
             isys.losetup(path, file)
@@ -2042,7 +2731,7 @@ def allocateLoopback(file):
             continue
         break
     if found:
-        return dev
+        return path
     return None
 
 def ext2FormatFilesystem(argList, messageFile, windowCreator, mntpoint):
@@ -2061,7 +2750,17 @@ def ext2FormatFilesystem(argList, messageFile, windowCreator, mntpoint):
         os.dup2(fd, 2)
         os.close(p[1])
         os.close(fd)
-        os.execvp(argList[0], argList)
+
+        env = os.environ
+        configs = [ "/tmp/updates/mke2fs.conf",
+                    "/etc/mke2fs.conf",
+                  ]
+        for config in configs:
+            if os.access(config, os.R_OK):
+                env['MKE2FS_CONFIG'] = config
+                break
+
+        os.execvpe(argList[0], argList, env)
         log.critical("failed to exec %s", argList)
         os._exit(1)
 
@@ -2130,7 +2829,7 @@ def getDiskPart(dev):
     cut = len(dev)
     if (dev.startswith('rd/') or dev.startswith('ida/') or
             dev.startswith('cciss/') or dev.startswith('sx8/') or
-            dev.startswith('mapper/')):
+            dev.startswith('mapper/') or dev.startswith('mmcblk')):
         if dev[-2] == 'p':
             cut = -1
         elif dev[-3] == 'p':
index bfda36e63429c523b6516f31756db45036b889cb..89230eb51bbe8479442204f605b1f1a0f9d0e4b0 100644 (file)
@@ -28,7 +28,7 @@ from optparse import OptionParser
 
 import isys
 import users
-import inutil
+import iutil
 import dispatch
 from flags import flags
 from constants import *
@@ -81,7 +81,7 @@ def setupLoggingFromOpts(opts):
             logger.addSysLogHandler(log, opts.syslog)
 
 def checkMemory():
-    if inutil.memInstalled() < isys.MIN_RAM:
+    if iutil.memInstalled() < isys.MIN_RAM:
         from snack import SnackScreen, ButtonChoiceWindow
 
         screen = SnackScreen()
@@ -110,7 +110,7 @@ class Pomona:
         if bootreq:
             autorequests.extend(bootreq)
 
-        (minswap, maxswap) = inutil.swapSuggestion()
+        (minswap, maxswap) = iutil.swapSuggestion()
         autorequests.append((None, "swap", minswap, maxswap, 1, 1, 1))
 
         if doClear:
index 6956efbce0ad50570384d45757d5173ae43b93d9..d1762e7f388b9c5e84592c9cb58b868e40ccf75d 100644 (file)
@@ -42,7 +42,7 @@ class InstallData:
         self.rootPassword = { "password": "" }
         self.fsset.reset()
         self.diskset = partedUtils.DiskSet(pomona)
-        self.partitions = partitions.Partitions()
+        self.partitions = partitions.Partitions(pomona)
         self.bootloader = bootloader.getBootloader()
         self.rootParts = None
 
index 69e02f1e125e920504e1e010f6743eda01097675..ec5ad5e408b59e57da08ef169065081999ada407 100644 (file)
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <unistd.h>
-#include <fcntl.h>
 
 #include "imount.h"
+#include "sundries.h"
 
 #define _(foo) foo
 
 static int mkdirIfNone(char * directory);
 
-int doPwMount(char *dev, char *where, char *fs, char *options) {
-    int rc, child, status;
+static int readFD(int fd, char **buf) {
+    char *p;
+    size_t size = 4096;
+    int s, filesize = 0;
+
+    *buf = calloc(4096, sizeof (char));
+    if (*buf == NULL)
+        abort();
+
+    do {
+        p = &(*buf)[filesize];
+        s = read(fd, p, 4096);
+        if (s < 0)
+            break;
+
+        filesize += s;
+        if (s == 0)
+           break;
+
+        size += s;
+        *buf = realloc(*buf, size);
+        if (*buf == NULL)
+            abort();
+    } while (1);
+
+    if (filesize == 0 && s < 0) {
+        free(*buf);
+        *buf = NULL;
+        return -1;
+    }
+
+    return filesize;
+}
+
+int doPwMount(char *dev, char *where, char *fs, char *options, char **err) {
+    int rc, child, status, pipefd[2];
     char *opts = NULL, *device;
 
-    if (mkdirChain(where)) {
+    if (mkdirChain(where))
         return IMOUNT_ERR_ERRNO;
-    }
 
     if (strstr(fs, "nfs")) {
-        if (options)
-            rc = asprintf(&opts, "%s,nolock", options);
-        else
+        if (options) {
+            if (asprintf(&opts, "%s,nolock", options) == -1) {
+                fprintf(stderr, "%s: %d: %s\n", __func__, __LINE__,
+                        strerror(errno));
+                fflush(stderr);
+                abort();
+            }
+        } else {
             opts = strdup("nolock");
+        }
         device = strdup(dev);
     } else {
         if ((options && strstr(options, "bind") == NULL) &&
-            strncmp(dev, "LABEL=", 6) && strncmp(dev, "UUID=", 5) && *dev != '/')
-           rc = asprintf(&device, "/dev/%s", dev);
-        else
+            strncmp(dev, "LABEL=", 6) && strncmp(dev, "UUID=", 5) &&
+            *dev != '/') {
+           if (asprintf(&device, "/dev/%s", dev) == -1) {
+               fprintf(stderr, "%s: %d: %s\n", __func__, __LINE__,
+                       strerror(errno));
+               fflush(stderr);
+               abort();
+           }
+        } else {
            device = strdup(dev);
+        }
         if (options)
             opts = strdup(options);
     }
 
+    if (pipe(pipefd))
+        return IMOUNT_ERR_ERRNO;
 
     if (!(child = fork())) {
         int fd;
 
-        /* Close off all these filehandles since we don't want errors
-         * spewed to tty1.
+        close(pipefd[0]);
+
+        /* Close stdin entirely, redirect stdout to tty5, and redirect stderr
+         * to a pipe so we can put error messages into exceptions.  We'll
+         * only use these messages should mount also return an error code.
          */
         fd = open("/dev/tty5", O_RDONLY);
         close(STDIN_FILENO);
@@ -73,25 +124,30 @@ int doPwMount(char *dev, char *where, char *fs, char *options) {
         fd = open("/dev/tty5", O_WRONLY);
         close(STDOUT_FILENO);
         dup2(fd, STDOUT_FILENO);
-        close(STDERR_FILENO);
-        dup2(fd, STDERR_FILENO);
-        close(fd);
+
+        dup2(pipefd[1], STDERR_FILENO);
 
         if (opts) {
-            fprintf(stderr, "Running... /bin/mount -n -t %s -o %s %s %s\n",
+            fprintf(stdout, "Running... /bin/mount -n -t %s -o %s %s %s\n",
                     fs, opts, device, where);
             rc = execl("/bin/mount",
                        "/bin/mount", "-n", "-t", fs, "-o", opts, device, where, NULL);
             exit(1);
         }
         else {
-            fprintf(stderr, "Running... /bin/mount -n -t %s %s %s\n",
+            fprintf(stdout, "Running... /bin/mount -n -t %s %s %s\n",
                     fs, device, where);
             rc = execl("/bin/mount", "/bin/mount", "-n", "-t", fs, device, where, NULL);
             exit(1);
         }
     }
 
+    close(pipefd[1]);
+
+    if (err != NULL)
+        rc = readFD(pipefd[0], err);
+
+    close(pipefd[0]);
     waitpid(child, &status, 0);
 
     free(opts);
@@ -102,6 +158,27 @@ int doPwMount(char *dev, char *where, char *fs, char *options) {
     return 0;
 }
 
+int doMultiMount(char *dev, char *where, char **fstypes, char *options, char **err) {
+    int retval = 0, i;
+
+    for (i = 0; fstypes[i]; i++) {
+        /* If we made a previous call to mount and it returned an error message,
+         * get rid of it now.  We only want to preserve the error from the last
+         * fstype.
+         */
+        if (err && *err && **err) {
+            free(*err);
+            *err = NULL;
+        }
+
+        retval = doPwMount(dev, where, fstypes[i], options, err);
+        if (!retval)
+            return retval;
+    }
+
+    return retval;
+}
+
 int mkdirChain(char * origChain) {
     char * chain;
     char * chptr;
index 50ee116008588fb36d105acb8835f382fef637ca..56f6deaf3d7c7c70db1eb532253045372741b699 100644 (file)
@@ -29,7 +29,8 @@
 #define IMOUNT_BIND    2
 #define IMOUNT_REMOUNT 4
 
-int doPwMount(char *dev, char *where, char *fs, char *options);
+int doPwMount(char *dev, char *where, char *fs, char *options, char **err);
+int doMultiMount(char *dev, char *where, char **fstypes, char *options, char **err);
 int mkdirChain(char * origChain);
 
 #endif
index cedfb766179f28b82aaa92b067deb27fe9f10266..f56e7139b5620ccc7dc615705f63cdcd8c4eed65 100644 (file)
 #include <popt.h>
 /* Need to tell loop.h what the actual dev_t type is. */
 #undef dev_t
-#if defined(__alpha) || (defined(__sparc__) && defined(__arch64__))
-#define dev_t unsigned int
-#else
-#if defined(__x86_64__)
-#define dev_t unsigned long
-#else
 #define dev_t unsigned short
-#endif
-#endif
 #include <linux/loop.h>
 #undef dev_t
 #define dev_t dev_t
 #include <scsi/scsi_ioctl.h>
 #include <sys/vt.h>
 #include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
 #include <linux/fb.h>
 #include <libintl.h>
+#ifdef USESELINUX
+#include <selinux/selinux.h>
+#endif
 #include <libgen.h>
 #include <linux/major.h>
 #include <linux/raid/md_u.h>
 #include <linux/raid/md_p.h>
 #include <signal.h>
+#include <execinfo.h>
 
 #include <blkid/blkid.h>
-#include <blkid/blkid_types.h>
 
 #include "isys.h"
 #include "imount.h"
-#include "net.h"
 #include "smp.h"
 #include "lang.h"
-#include "wireless.h"
 #include "eddsupport.h"
+#include "imount.h"
 
 #ifndef CDROMEJECT
 #define CDROMEJECT 0x5309
 #endif
 
-static PyObject * doGetOpt(PyObject * s, PyObject * args);
-/*static PyObject * doInsmod(PyObject * s, PyObject * args);
-static PyObject * doRmmod(PyObject * s, PyObject * args);*/
 static PyObject * doMount(PyObject * s, PyObject * args);
 static PyObject * doUMount(PyObject * s, PyObject * args);
 static PyObject * smpAvailable(PyObject * s, PyObject * args);
@@ -89,26 +82,29 @@ static PyObject * doLoSetup(PyObject * s, PyObject * args);
 static PyObject * doUnLoSetup(PyObject * s, PyObject * args);
 static PyObject * doLoChangeFd(PyObject * s, PyObject * args);
 static PyObject * doDdFile(PyObject * s, PyObject * args);
+static PyObject * doWipeRaidSuperblock(PyObject * s, PyObject * args);
+static PyObject * doGetRaidSuperblock(PyObject * s, PyObject * args);
+static PyObject * doGetRaidChunkSize(PyObject * s, PyObject * args);
 static PyObject * doDevSpaceFree(PyObject * s, PyObject * args);
+static PyObject * doResetResolv(PyObject * s, PyObject * args);
 static PyObject * doLoadKeymap(PyObject * s, PyObject * args);
-static PyObject * doClobberExt2(PyObject * s, PyObject * args);
+static PyObject * doClobberExt2 (PyObject * s, PyObject * args);
 static PyObject * doReadE2fsLabel(PyObject * s, PyObject * args);
 static PyObject * doExt2Dirty(PyObject * s, PyObject * args);
 static PyObject * doExt2HasJournal(PyObject * s, PyObject * args);
 static PyObject * doEjectCdrom(PyObject * s, PyObject * args);
 static PyObject * doVtActivate(PyObject * s, PyObject * args);
-static PyObject * doisPsudoTTY(PyObject * s, PyObject * args);
+static PyObject * doisPseudoTTY(PyObject * s, PyObject * args);
 static PyObject * doisVioConsole(PyObject * s);
 static PyObject * doSync(PyObject * s, PyObject * args);
 static PyObject * doisIsoImage(PyObject * s, PyObject * args);
 static PyObject * getFramebufferInfo(PyObject * s, PyObject * args);
 static PyObject * printObject(PyObject * s, PyObject * args);
-static PyObject * py_bind_textdomain_codeset(PyObject * o, PyObject * args);
-static PyObject * isWireless(PyObject * s, PyObject * args);
 static PyObject * doProbeBiosDisks(PyObject * s, PyObject * args);
 static PyObject * doGetBiosDisk(PyObject * s, PyObject * args);
-static PyObject * doGetBlkidData(PyObject * s, PyObject * args);
 static PyObject * doSegvHandler(PyObject *s, PyObject *args);
+static PyObject * doGetBlkidData(PyObject * s, PyObject * args);
+static PyObject * doGetDeviceByToken(PyObject *s, PyObject *args);
 
 static PyMethodDef isysModuleMethods[] = {
     { "ejectcdrom", (PyCFunction) doEjectCdrom, METH_VARARGS, NULL },
@@ -117,31 +113,33 @@ static PyMethodDef isysModuleMethods[] = {
     { "e2fslabel", (PyCFunction) doReadE2fsLabel, METH_VARARGS, NULL },
     { "e2fsclobber", (PyCFunction) doClobberExt2, METH_VARARGS, NULL },
     { "devSpaceFree", (PyCFunction) doDevSpaceFree, METH_VARARGS, NULL },
+    { "getraidsb", (PyCFunction) doGetRaidSuperblock, METH_VARARGS, NULL },
+    { "wiperaidsb", (PyCFunction) doWipeRaidSuperblock, METH_VARARGS, NULL },
+    { "getraidchunk", (PyCFunction) doGetRaidChunkSize, METH_VARARGS, NULL },
     { "lochangefd", (PyCFunction) doLoChangeFd, METH_VARARGS, NULL },
     { "losetup", (PyCFunction) doLoSetup, METH_VARARGS, NULL },
     { "unlosetup", (PyCFunction) doUnLoSetup, METH_VARARGS, NULL },
     { "ddfile", (PyCFunction) doDdFile, METH_VARARGS, NULL },
-    { "getopt", (PyCFunction) doGetOpt, METH_VARARGS, NULL },
     { "mount", (PyCFunction) doMount, METH_VARARGS, NULL },
     { "smpavailable", (PyCFunction) smpAvailable, METH_VARARGS, NULL },
     { "htavailable", (PyCFunction) htAvailable, METH_VARARGS, NULL },
     { "umount", (PyCFunction) doUMount, METH_VARARGS, NULL },
+    { "resetresolv", (PyCFunction) doResetResolv, METH_VARARGS, NULL },
     { "swapon",  (PyCFunction) doSwapon, METH_VARARGS, NULL },
     { "swapoff",  (PyCFunction) doSwapoff, METH_VARARGS, NULL },
     { "loadKeymap", (PyCFunction) doLoadKeymap, METH_VARARGS, NULL },
     { "vtActivate", (PyCFunction) doVtActivate, METH_VARARGS, NULL},
-    { "isPsudoTTY", (PyCFunction) doisPsudoTTY, METH_VARARGS, NULL},
+    { "isPseudoTTY", (PyCFunction) doisPseudoTTY, METH_VARARGS, NULL},
     { "isVioConsole", (PyCFunction) doisVioConsole, METH_NOARGS, NULL},
     { "sync", (PyCFunction) doSync, METH_VARARGS, NULL},
     { "isisoimage", (PyCFunction) doisIsoImage, METH_VARARGS, NULL},
     { "fbinfo", (PyCFunction) getFramebufferInfo, METH_VARARGS, NULL},
     { "printObject", (PyCFunction) printObject, METH_VARARGS, NULL},
-    { "bind_textdomain_codeset", (PyCFunction) py_bind_textdomain_codeset, METH_VARARGS, NULL},
-    { "isWireless", (PyCFunction) isWireless, METH_VARARGS, NULL },
     { "biosDiskProbe", (PyCFunction) doProbeBiosDisks, METH_VARARGS,NULL},
     { "getbiosdisk",(PyCFunction) doGetBiosDisk, METH_VARARGS,NULL},
-    { "getblkid", (PyCFunction) doGetBlkidData, METH_VARARGS, NULL },
     { "handleSegv", (PyCFunction) doSegvHandler, METH_VARARGS, NULL },
+    { "getblkid", (PyCFunction) doGetBlkidData, METH_VARARGS, NULL },
+    { "getdevicebytoken", (PyCFunction) doGetDeviceByToken, METH_VARARGS, NULL },
     { NULL, NULL, 0, NULL }
 } ;
 
@@ -230,150 +228,6 @@ static PyObject * doLoSetup(PyObject * s, PyObject * args) {
     return Py_None;
 }
 
-static PyObject * doGetOpt(PyObject * s, PyObject * pyargs) {
-    PyObject * argList, * longArgs, * strObject;
-    PyObject * retList, * retArgs;
-    char * shortArgs;
-    struct poptOption * options;
-    int numOptions, i, rc;
-    char * ch;
-    poptContext optCon;
-    char * str;
-    char * error;
-    const char ** argv;
-    char strBuf[3];
-
-    if (!PyArg_ParseTuple(pyargs, "OsO", &argList, &shortArgs, &longArgs))
-       return NULL;
-
-    if (!(PyList_Check(argList))) {
-       PyErr_SetString(PyExc_TypeError, "list expected");
-    }
-    if (!(PyList_Check(longArgs))) {
-       PyErr_SetString(PyExc_TypeError, "list expected");
-    }
-
-    numOptions = PyList_Size(longArgs);
-    for (ch = shortArgs; *ch; ch++)
-       if (*ch != ':') numOptions++;
-
-    options = alloca(sizeof(*options) * (numOptions + 1));
-
-    ch = shortArgs;
-    numOptions = 0;
-    while (*ch) {
-        options[numOptions].shortName = *ch++;
-        options[numOptions].longName = NULL;
-       options[numOptions].val = 0;
-       options[numOptions].descrip = NULL;
-       options[numOptions].argDescrip = NULL;
-       options[numOptions].arg = NULL;
-       if (*ch == ':') {
-           options[numOptions].argInfo = POPT_ARG_STRING;
-           ch++;
-       } else {
-           options[numOptions].argInfo = POPT_ARG_NONE;
-       }
-
-       options[numOptions].val = numOptions + 1;
-
-       numOptions++;
-    }
-
-    for (i = 0; i < PyList_Size(longArgs); i++) {
-        options[numOptions].shortName = 0;
-       options[numOptions].val = 0;
-       options[numOptions].descrip = NULL;
-       options[numOptions].argDescrip = NULL;
-       options[numOptions].arg = NULL;
-
-        strObject = PyList_GetItem(longArgs, i);
-       str = PyString_AsString(strObject);
-       if (!str) return NULL;
-
-       if (str[strlen(str) - 1] == '=') {
-           str = strcpy(alloca(strlen(str) + 1), str);
-           str[strlen(str) - 1] = '\0';
-           options[numOptions].argInfo = POPT_ARG_STRING;
-       } else {
-           options[numOptions].argInfo = POPT_ARG_NONE;
-       }
-
-       options[numOptions].val = numOptions + 1;
-       options[numOptions].longName = str;
-
-       numOptions++;
-    }
-
-    memset(options + numOptions, 0, sizeof(*options));
-
-    argv = alloca(sizeof(*argv) * (PyList_Size(argList) + 1));
-    for (i = 0; i < PyList_Size(argList); i++) {
-        strObject = PyList_GetItem(argList, i);
-       str = PyString_AsString(strObject);
-       if (!str) return NULL;
-
-       argv[i] = str;
-    }
-
-    argv[i] = NULL;
-
-    optCon = poptGetContext("", PyList_Size(argList), argv,
-                           options, POPT_CONTEXT_KEEP_FIRST);
-    retList = PyList_New(0);
-    retArgs = PyList_New(0);
-
-    while ((rc = poptGetNextOpt(optCon)) >= 0) {
-       const char * argument;
-
-       rc--;
-
-       if (options[rc].argInfo == POPT_ARG_STRING)
-           argument = poptGetOptArg(optCon);
-       else
-           argument = NULL;
-
-       if (options[rc].longName) {
-           str = alloca(strlen(options[rc].longName) + 3);
-           sprintf(str, "--%s", options[rc].longName);
-       } else {
-           str = strBuf;
-           sprintf(str, "-%c", options[rc].shortName);
-       }
-
-       if (argument) {
-           argument = strcpy(alloca(strlen(argument) + 1), argument);
-           PyList_Append(retList,
-                           Py_BuildValue("(ss)", str, argument));
-       } else {
-           PyList_Append(retList, Py_BuildValue("(ss)", str, ""));
-       }
-    }
-
-    if (rc < -1) {
-       i = strlen(poptBadOption(optCon, POPT_BADOPTION_NOALIAS)) +
-           strlen(poptStrerror(rc));
-
-       error = alloca(i) + 50;
-
-       sprintf(error, "bad argument %s: %s\n",
-               poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
-               poptStrerror(rc));
-
-       PyErr_SetString(PyExc_TypeError, error);
-       return NULL;
-    }
-
-    argv = (const char **) poptGetArgs(optCon);
-    for (i = 0; argv && argv[i]; i++) {
-       PyList_Append(retArgs, PyString_FromString(argv[i]));
-    }
-
-    poptFreeContext(optCon);
-
-    return Py_BuildValue("(OO)", retList, retArgs);
-}
-
 static PyObject * doUMount(PyObject * s, PyObject * args) {
     char * fs;
 
@@ -389,20 +243,20 @@ static PyObject * doUMount(PyObject * s, PyObject * args) {
 }
 
 static PyObject * doMount(PyObject * s, PyObject * args) {
-    char *fs, *device, *mntpoint, *flags = NULL;
+    char *err = NULL, *fs, *device, *mntpoint, *flags = NULL;
     int rc;
 
     if (!PyArg_ParseTuple(args, "sss|z", &fs, &device, &mntpoint,
                          &flags)) return NULL;
 
-    rc = doPwMount(device, mntpoint, fs, flags);
+    rc = doPwMount(device, mntpoint, fs, flags, &err);
     if (rc == IMOUNT_ERR_ERRNO)
        PyErr_SetFromErrno(PyExc_SystemError);
     else if (rc) {
         PyObject *tuple = PyTuple_New(2);
 
         PyTuple_SetItem(tuple, 0, PyInt_FromLong(rc));
-        PyTuple_SetItem(tuple, 1, PyString_FromString("mount failed"));
+        PyTuple_SetItem(tuple, 1, PyString_FromString(err));
         PyErr_SetObject(PyExc_SystemError, tuple);
     }
 
@@ -468,6 +322,121 @@ void init_isys(void) {
     PyDict_SetItemString(d, "EARLY_SWAP_RAM", PyInt_FromLong(EARLY_SWAP_RAM));
 }
 
+static PyObject * doResetResolv(PyObject * s, PyObject * args) {
+    if (!PyArg_ParseTuple(args, "")) {
+        return NULL;
+    }
+
+    /* reinit the resolver so DNS changes take affect */
+    res_init();
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject * doWipeRaidSuperblock(PyObject * s, PyObject * args) {
+    int fd;
+    unsigned long size;
+    struct mdp_super_t * sb;
+
+    if (!PyArg_ParseTuple(args, "i", &fd)) return NULL;
+
+    if (ioctl(fd, BLKGETSIZE, &size)) {
+       PyErr_SetFromErrno(PyExc_SystemError);
+       return NULL;
+    }
+
+    /* put the size in 1k blocks */
+    size >>= 1;
+
+    if (lseek64(fd, ((off64_t) 512) * (off64_t) MD_NEW_SIZE_SECTORS(size), SEEK_SET) < 0) {
+       PyErr_SetFromErrno(PyExc_SystemError);
+       return NULL;
+    }
+
+    sb = malloc(sizeof(mdp_super_t));
+    sb = memset(sb, '\0', sizeof(mdp_super_t));
+
+    if (write(fd, sb, sizeof(sb)) != sizeof(sb)) {
+       PyErr_SetFromErrno(PyExc_SystemError);
+       return NULL;
+    }
+
+    return Py_None;
+}
+
+static PyObject * doGetRaidSuperblock(PyObject * s, PyObject * args) {
+    int fd;
+    unsigned long size;
+    mdp_super_t sb;
+    char uuid[36];
+
+    if (!PyArg_ParseTuple(args, "i", &fd)) return NULL;
+
+    if (ioctl(fd, BLKGETSIZE, &size)) {
+       PyErr_SetFromErrno(PyExc_SystemError);
+       return NULL;
+    }
+
+    /* put the size in 1k blocks */
+    size >>= 1;
+
+    if (lseek64(fd, ((off64_t) 512) * (off64_t) MD_NEW_SIZE_SECTORS(size), SEEK_SET) < 0) {
+       PyErr_SetFromErrno(PyExc_SystemError);
+       return NULL;
+    }
+
+    if (read(fd, &sb, sizeof(sb)) != sizeof(sb)) {
+       PyErr_SetFromErrno(PyExc_SystemError);
+       return NULL;
+    }
+
+    if (sb.md_magic != MD_SB_MAGIC) {
+       PyErr_SetString(PyExc_ValueError, "bad md magic on device");
+       return NULL;
+    }
+
+    sprintf(uuid, "%08x:%08x:%08x:%08x", sb.set_uuid0, sb.set_uuid1,
+            sb.set_uuid2, sb.set_uuid3);
+
+    return Py_BuildValue("(iisiiii)", sb.major_version, sb.minor_version,
+                        uuid, sb.level, sb.nr_disks, sb.raid_disks,
+                        sb.md_minor);
+}
+
+static PyObject * doGetRaidChunkSize(PyObject * s, PyObject * args) {
+    int fd;
+    unsigned long size;
+    mdp_super_t sb;
+
+    if (!PyArg_ParseTuple(args, "i", &fd)) return NULL;
+
+    if (ioctl(fd, BLKGETSIZE, &size)) {
+       PyErr_SetFromErrno(PyExc_SystemError);
+       return NULL;
+    }
+
+    /* put the size in 1k blocks */
+    size >>= 1;
+
+    if (lseek64(fd, ((off64_t) 512) * (off64_t) MD_NEW_SIZE_SECTORS(size), SEEK_SET) < 0) {
+       PyErr_SetFromErrno(PyExc_SystemError);
+       return NULL;
+    }
+
+    if (read(fd, &sb, sizeof(sb)) != sizeof(sb)) {
+       PyErr_SetFromErrno(PyExc_SystemError);
+       return NULL;
+    }
+
+    if (sb.md_magic != MD_SB_MAGIC) {
+       PyErr_SetString(PyExc_ValueError, "bad md magic on device");
+       return NULL;
+    }
+
+    return Py_BuildValue("i", sb.chunk_size / 1024);
+}
+
 static int get_bits(unsigned long long v) {
     int  b = 0;
 
@@ -592,7 +561,6 @@ static PyObject * doExt2Dirty(PyObject * s, PyObject * args) {
 
     return Py_BuildValue("i", !clean);
 }
-
 static PyObject * doExt2HasJournal(PyObject * s, PyObject * args) {
     char * device;
     ext2_filsys fsys;
@@ -642,7 +610,7 @@ static PyObject * doVtActivate(PyObject * s, PyObject * args) {
     return Py_None;
 }
 
-static PyObject * doisPsudoTTY(PyObject * s, PyObject * args) {
+static PyObject * doisPseudoTTY(PyObject * s, PyObject * args) {
     int fd;
     struct stat sb;
 
@@ -701,18 +669,6 @@ static PyObject * getFramebufferInfo(PyObject * s, PyObject * args) {
     return Py_BuildValue("(iii)", fb.xres, fb.yres, fb.bits_per_pixel);
 }
 
-static PyObject * isWireless(PyObject * s, PyObject * args) {
-    char *dev;
-    int ret;
-
-    if (!PyArg_ParseTuple(args, "s", &dev))
-       return NULL;
-
-    ret = is_wireless_interface(dev);
-
-    return Py_BuildValue("i", ret);
-}
-
 static PyObject * printObject (PyObject * o, PyObject * args) {
     PyObject * obj;
     char buf[256];
@@ -726,22 +682,6 @@ static PyObject * printObject (PyObject * o, PyObject * args) {
     return PyString_FromString(buf);
 }
 
-static PyObject *
-py_bind_textdomain_codeset(PyObject * o, PyObject * args) {
-    char *domain, *codeset, *ret;
-
-    if (!PyArg_ParseTuple(args, "ss", &domain, &codeset))
-       return NULL;
-
-    ret = bind_textdomain_codeset(domain, codeset);
-
-    if (ret)
-       return PyString_FromString(ret);
-
-    PyErr_SetFromErrno(PyExc_SystemError);
-    return NULL;
-}
-
 static PyObject * doProbeBiosDisks(PyObject * s, PyObject * args) {
     if (!PyArg_ParseTuple(args, "")) return NULL;
 
@@ -762,6 +702,42 @@ static PyObject * doGetBiosDisk(PyObject * s, PyObject * args) {
     return Py_None;
 }
 
+static PyObject * doSegvHandler(PyObject *s, PyObject *args) {
+    void *array[20];
+    size_t size;
+    char **strings;
+    size_t i;
+
+    signal(SIGSEGV, SIG_DFL); /* back to default */
+
+    size = backtrace (array, 20);
+    strings = backtrace_symbols (array, size);
+
+    printf ("Anaconda received SIGSEGV!.  Backtrace:\n");
+    for (i = 0; i < size; i++)
+        printf ("%s\n", strings[i]);
+
+    free (strings);
+    exit(1);
+}
+
+static PyObject *doGetDeviceByToken(PyObject *s, PyObject *args) {
+    blkid_cache cache;
+    char *token, *value, *dev;
+
+    if (!PyArg_ParseTuple(args, "ss", &token, &value)) return NULL;
+
+    blkid_get_cache(&cache, NULL);
+
+    dev = blkid_get_devname(cache, token, value);
+    if (dev == NULL) {
+        Py_INCREF(Py_None);
+        return Py_None;
+    } else {
+        return Py_BuildValue("s", dev);
+    }
+}
+
 static PyObject * doGetBlkidData(PyObject * s, PyObject * args) {
     char * dev, * key;
     blkid_cache cache;
@@ -790,23 +766,4 @@ static PyObject * doGetBlkidData(PyObject * s, PyObject * args) {
     return Py_None;
 }
 
-static PyObject * doSegvHandler(PyObject *s, PyObject *args) {
-    void *array[20];
-    size_t size;
-    char **strings;
-    size_t i;
-
-    signal(SIGSEGV, SIG_DFL); /* back to default */
-
-    size = backtrace (array, 20);
-    strings = backtrace_symbols (array, size);
-
-    printf ("Pomona received SIGSEGV!.  Backtrace:\n");
-    for (i = 0; i < size; i++)
-        printf ("%s\n", strings[i]);
-
-    free (strings);
-    exit(1);
-}
-
 /* vim:set shiftwidth=4 softtabstop=4: */
index 6cd54bb4a748562996f0f286bbff4f9526d56c6e..7104c6c191b7f71eb1a7d4b9305ebb01b428f105 100644 (file)
@@ -30,7 +30,7 @@ import socket
 import stat
 import posix
 import sys
-import inutil
+import iutil
 import warnings
 import resource
 import re
@@ -53,6 +53,190 @@ EARLY_SWAP_RAM = _isys.EARLY_SWAP_RAM
 def pathSpaceAvailable(path):
     return _isys.devSpaceFree(path)
 
+
+mdadmOutput = "/tmp/mdadmout"
+
+## An error occured when running mdadm.
+class MdadmError(Exception):
+    ## The constructor.
+    # @param args The arguments passed to the mdadm command.
+    # @param name The the name of the RAID device used in the mdadm command.
+    def __init__(self, args, name=None):
+        self.args = args
+        self.name = name
+        self.log = self.getCmdOutput()
+
+    ## Get the output of the last mdadm command run.
+    # @return The formatted output of the mdadm command which caused an error.
+    def getCmdOutput(self):
+        f = open(mdadmOutput, "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,)
+        command = "mdadm " + string.join(self.args, " ")
+        return "'%s' failed%s\nLog:\n%s" % (command, s, self.log)
+
+def _mdadm(*args):
+    try:
+        lines = iutil.execWithCapture("mdadm", args, stderr = mdadmOutput)
+        lines = string.split(lines, '\n')
+        lines = reduce(lambda x,y: x + [y.strip(),], lines, [])
+        return lines
+    except:
+        raise MdadmError, args
+
+def _getRaidInfo(drive):
+    log.info("mdadm -E %s" % (drive,))
+    try:
+        lines = _mdadm("-E", drive)
+    except MdadmError:
+        ei = sys.exc_info()
+        ei[1].name = drive
+        raise ei[0], ei[1], ei[2]
+
+    info = {
+            'major': "-1",
+            'minor': "-1",
+            'uuid' : "",
+            'level': -1,
+            'nrDisks': -1,
+            'totalDisks': -1,
+            'mdMinor': -1,
+        }
+
+    for line in lines:
+        vals = string.split(string.strip(line), ' : ')
+        if len(vals) != 2:
+            continue
+        if vals[0] == "Version":
+            vals = string.split(vals[1], ".")
+            info['major'] = vals[0]
+            info['minor'] = vals[1]
+        elif vals[0] == "UUID":
+            info['uuid'] = vals[1]
+        elif vals[0] == "Raid Level":
+            info['level'] = int(vals[1][4:])
+        elif vals[0] == "Raid Devices":
+            info['nrDisks'] = int(vals[1])
+        elif vals[0] == "Total Devices":
+            info['totalDisks'] = int(vals[1])
+        elif vals[0] == "Preferred Minor":
+            info['mdMinor'] = int(vals[1])
+        else:
+            continue
+
+    if info['uuid'] == "":
+        raise ValueError, info
+
+    return info
+
+def _stopRaid(mdDevice):
+    log.info("mdadm --stop %s" % (mdDevice,))
+    try:
+        _mdadm("--stop", mdDevice)
+    except MdadmError:
+        ei = sys.exc_info()
+        ei[1].name = mdDevice
+        raise ei[0], ei[1], ei[2]
+
+def raidstop(mdDevice):
+    log.info("stopping raid device %s" %(mdDevice,))
+    if raidCount.has_key (mdDevice):
+        if raidCount[mdDevice] > 1:
+            raidCount[mdDevice] = raidCount[mdDevice] - 1
+            return
+        del raidCount[mdDevice]
+
+    devInode = "/dev/%s" % mdDevice
+
+    try:
+        _stopRaid(devInode)
+    except:
+        pass
+
+def _startRaid(mdDevice, mdMinor, uuid):
+    log.info("mdadm -A --uuid=%s --super-minor=%s %s" % (uuid, mdMinor, mdDevice))
+    try:
+        _mdadm("-A", "--uuid=%s" % (uuid,), "--super-minor=%s" % (mdMinor,), \
+                mdDevice)
+    except MdadmError:
+        ei = sys.exc_info()
+        ei[1].name = mdDevice
+        raise ei[0], ei[1], ei[2]
+
+def raidstart(mdDevice, aMember):
+    log.info("starting raid device %s" %(mdDevice,))
+    if raidCount.has_key(mdDevice) and raidCount[mdDevice]:
+        raidCount[mdDevice] = raidCount[mdDevice] + 1
+        return
+
+    raidCount[mdDevice] = 1
+
+    mdInode = "/dev/%s" % mdDevice
+    mbrInode = "/dev/%s" % aMember
+
+    if os.path.exists(mdInode):
+        minor = os.minor(os.stat(mdInode).st_rdev)
+    else:
+        minor = int(mdDevice[2:])
+    try:
+        info = _getRaidInfo(mbrInode)
+        if info.has_key('mdMinor'):
+            minor = info['mdMinor']
+        _startRaid(mdInode, minor, info['uuid'])
+    except:
+        pass
+
+## Remove the superblock from a RAID device.
+# @param device The complete path to the RAID device name to wipe.
+def wipeRaidSB(device):
+    try:
+        fd = os.open(device, os.O_WRONLY)
+    except OSError, e:
+        log.warning("error wiping raid device superblock for %s: %s", device, e)
+        return
+
+    try:
+        _isys.wiperaidsb(fd)
+    finally:
+        os.close(fd)
+    return
+
+## Get the raw superblock from a RAID device.
+# @param The basename of a RAID device to check.  This device node does not
+#        need to exist to begin with.
+# @return A RAID superblock in its raw on-disk format.
+def raidsb(mdDevice):
+    return raidsbFromDevice("/dev/%s" % mdDevice)
+
+## Get the superblock from a RAID device.
+# @param The full path to a RAID device name to check.  This device node must
+#        already exist.
+# @return A tuple of the contents of the RAID superblock, or ValueError on
+#         error.
+def raidsbFromDevice(device):
+    try:
+        info = _getRaidInfo(device)
+        return (info['major'], info['minor'], info['uuid'], info['level'],
+                info['nrDisks'], info['totalDisks'], info['mdMinor'])
+    except:
+        raise ValueError
+
+def getRaidChunkFromDevice(device):
+    fd = os.open(device, os.O_RDONLY)
+    rc = 64
+    try:
+        rc = _isys.getraidchunk(fd)
+    finally:
+        os.close(fd)
+    return rc
+
 ## Set up an already existing device node to be used as a loopback device.
 # @param device The full path to a device node to set up as a loopback device.
 # @param file The file to mount as loopback on device.
@@ -515,7 +699,7 @@ def vtActivate(num):
     _isys.vtActivate (num)
 
 def isPsudoTTY(fd):
-    return _isys.isPsudoTTY (fd)
+    return _isys.isPseudoTTY(fd)
 
 ## Flush filesystem buffers.
 def sync():
@@ -523,5 +707,4 @@ def sync():
 
 handleSegv = _isys.handleSegv
 printObject = _isys.printObject
-bind_textdomain_codeset = _isys.bind_textdomain_codeset
 isVioConsole = _isys.isVioConsole
similarity index 63%
rename from src/pomona/inutil.py
rename to src/pomona/iutil.py
index 3872cf5fee40c48b13187cda247b25a73ebb46e3..569ea8daf6b15765b854feb4c0407f4354790307 100644 (file)
@@ -1,10 +1,31 @@
-#!/usr/bin/python
-
-import os
-import subprocess
-import string
-import stat
+#
+# iutil.py - generic install utility functions
+#
+# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
+# Red Hat, Inc.  All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+# Author(s): Erik Troan <ewt@redhat.com>
+#
+
+import os, string, stat
+import os.path
 from errno import *
+import warnings
+import subprocess
+from flags import flags
 
 import logging
 log = logging.getLogger("pomona")
@@ -156,30 +177,39 @@ def execConsole():
     except OSError, (errno, msg):
         raise RuntimeError, "Error running /bin/sh: " + msg
 
-# try to keep 2.4 kernel swapper happy!
-def swapSuggestion(quiet=0):
-    mem = memInstalled()/1024
-    mem = ((mem/16)+1)*16
-    if not quiet:
-        log.info("Detected %sM of memory", mem)
+## Get the size of a directory and all its subdirectories.
+# @param dir The name of the directory to find the size of.
+# @return The size of the directory in kilobytes.
+def getDirSize(dir):
+    def getSubdirSize(dir):
+        # returns size in bytes
+        mydev = os.lstat(dir)[stat.ST_DEV]
 
-    if mem <= 256:
-        minswap = 256
-        maxswap = 512
-    else:
-        if mem > 1000:
-            minswap = 1000
-            maxswap = 2000
-        else:
-            minswap = mem
-            maxswap = 2*mem
+        dsize = 0
+        for f in os.listdir(dir):
+            curpath = '%s/%s' % (dir, f)
+            sinfo = os.lstat(curpath)
+            if stat.S_ISDIR(sinfo[stat.ST_MODE]):
+                if mydev == sinfo[stat.ST_DEV]:
+                    dsize += getSubdirSize(curpath)
+            elif stat.S_ISREG(sinfo[stat.ST_MODE]):
+                dsize += sinfo[stat.ST_SIZE]
+            else:
+                pass
 
-    if not quiet:
-        log.info("Swap attempt of %sM to %sM", minswap, maxswap)
+        return dsize
+    return getSubdirSize(dir)/1024
 
-    return (minswap, maxswap)
+## Get the amount of RAM not used by /tmp.
+# @return The amount of available memory in kilobytes.
+def memAvailable():
+    tram = memInstalled()
 
-# this is in kilobytes
+    ramused = getDirSize("/tmp")
+    return tram - ramused
+
+## Get the amount of RAM installed in the machine.
+# @return The amount of installed memory in kilobytes.
 def memInstalled():
     f = open("/proc/meminfo", "r")
     lines = f.readlines()
@@ -193,38 +223,33 @@ def memInstalled():
 
     return int(mem)
 
-# this is in kilobytes - returns amount of RAM not used by /tmp
-def memAvailable():
-    tram = memInstalled()
-
-    ramused = getDirSize("/tmp")
-    if os.path.isdir("/tmp/ramfs"):
-        ramused += getDirSize("/tmp/ramfs")
+## Suggest the size of the swap partition that will be created.
+# @param quiet Should size information be logged?
+# @return A tuple of the minimum and maximum swap size, in megabytes.
+def swapSuggestion(quiet=0):
+    mem = memInstalled()/1024
+    mem = ((mem/16)+1)*16
+    if not quiet:
+        log.info("Detected %sM of memory", mem)
 
-    return tram - ramused
+    if mem <= 256:
+        minswap = 256
+        maxswap = 512
+    else:
+        if mem > 2000:
+            minswap = 1000
+            maxswap = 2000 + mem
+        else:
+            minswap = mem
+            maxswap = 2*mem
 
-# return size of directory (and subdirs) in kilobytes
-def getDirSize(dir):
-    def getSubdirSize(dir):
-        # returns size in bytes
-        mydev = os.lstat(dir)[stat.ST_DEV]
-        dsize = 0
-        for f in os.listdir(dir):
-            curpath = '%s/%s' % (dir, f)
-            sinfo = os.lstat(curpath)
-            if stat.S_ISDIR(sinfo[stat.ST_MODE]):
-                if mydev == sinfo[stat.ST_DEV]:
-                    dsize += getSubdirSize(curpath)
-            elif stat.S_ISREG(sinfo[stat.ST_MODE]):
-                dsize += sinfo[stat.ST_SIZE]
-            else:
-                pass
-        return dsize
+    if not quiet:
+        log.info("Swap attempt of %sM to %sM", minswap, maxswap)
 
-    return getSubdirSize(dir)/1024
+    return (minswap, maxswap)
 
-# this is a mkdir that won't fail if a directory already exists and will
-# happily make all of the directories leading up to it.
+## Create a directory path.  Don't fail if the directory already exists.
+# @param dir The directory path to create.
 def mkdirChain(dir):
     try:
         os.makedirs(dir, 0755)
@@ -236,3 +261,73 @@ def mkdirChain(dir):
             pass
 
         log.error("could not create directory %s: %s" % (dir, msg))
+
+## Get the total amount of swap memory.
+# @return The total amount of swap memory in kilobytes, or 0 if unknown.
+def swapAmount():
+    f = open("/proc/meminfo", "r")
+    lines = f.readlines()
+    f.close()
+
+    for l in lines:
+        if l.startswith("SwapTotal:"):
+            fields = string.split(l)
+            return int(fields[1])
+    return 0
+
+## Copy a device node.
+# Copies a device node by looking at the device type, major and minor device
+# numbers, and doing a mknod on the new device name.
+#
+# @param src The name of the source device node.
+# @param dest The name of the new device node to create.
+def copyDeviceNode(src, dest):
+    filestat = os.lstat(src)
+    mode = filestat[stat.ST_MODE]
+    if stat.S_ISBLK(mode):
+        type = stat.S_IFBLK
+    elif stat.S_ISCHR(mode):
+        type = stat.S_IFCHR
+    else:
+        # XXX should we just fallback to copying normally?
+        raise RuntimeError, "Tried to copy %s which isn't a device node" % (src,)
+
+    os.mknod(dest, mode | type, filestat.st_rdev)
+
+# Architecture checking functions
+
+def isX86(bits=None):
+    arch = os.uname()[4]
+
+    # x86 platforms include:
+    #     i*86
+    #     athlon*
+    #     x86_64
+    #     amd*
+    #     ia32e
+    if bits is None:
+        if (arch.startswith('i') and arch.endswith('86')) or \
+           arch.startswith('athlon') or arch.startswith('amd') or \
+           arch == 'x86_64' or arch == 'ia32e':
+            return True
+    elif bits == 32:
+        if arch.startswith('i') and arch.endswith('86'):
+            return True
+    elif bits == 64:
+        if arch.startswith('athlon') or arch.startswith('amd') or \
+           arch == 'x86_64' or arch == 'ia32e':
+            return True
+
+    return False
+
+def getArch():
+    if isX86(bits=32):
+        return 'i386'
+    elif isX86(bits=64):
+        return 'x86_64'
+    else:
+        return os.uname()[4]
+
+def isConsoleOnVirtualTerminal():
+    # XXX PJFIX is there some way to ask the kernel this instead?
+    return not flags.serial
diff --git a/src/pomona/lvm.py b/src/pomona/lvm.py
new file mode 100644 (file)
index 0000000..bd708d3
--- /dev/null
@@ -0,0 +1,608 @@
+#
+# lvm.py - lvm probing control
+#
+# Copyright (C) 2002  Red Hat, Inc.  All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+# Author(s): Jeremy Katz <katzj@redhat.com>
+#
+
+import os
+import sys
+import iutil
+import string
+import math
+import isys
+import re
+
+from flags import flags
+
+import logging
+log = logging.getLogger("pomona")
+
+from constants import *
+
+MAX_LV_SLOTS=256
+
+lvmDevicePresent = 0
+
+from errors import *
+
+def has_lvm():
+    global lvmDevicePresent
+
+    if not (os.access("/usr/sbin/lvm", os.X_OK) or
+            os.access("/sbin/lvm", os.X_OK)):
+        return
+
+    f = open("/proc/devices", "r")
+    lines = f.readlines()
+    f.close()
+
+    for line in lines:
+        try:
+            (dev, name) = line[:-1].split(' ', 2)
+        except:
+            continue
+        if name == "device-mapper":
+            lvmDevicePresent = 1
+            break
+    return lvmDevicePresent
+# now check to see if lvm is available
+has_lvm()
+
+def lvmExec(*args):
+    try:
+        return iutil.execWithRedirect("lvm", args, stdout = lvmErrorOutput,
+            stderr = lvmErrorOutput, searchPath = 1)
+    except Exception, e:
+        log.error("error running lvm command: %s" %(e,))
+        raise LvmError, args[0]
+
+def lvmCapture(*args):
+    try:
+        lvmout = iutil.execWithCapture("lvm", args, stderr = lvmErrorOutput)
+        lines = []
+        for line in lvmout.split("\n"):
+            lines.append(line.strip().split(':'))
+        return lines
+    except Exception, e:
+        log.error("error running lvm command: %s" %(e,))
+        raise LvmError, args[0]
+
+def vgscan():
+    """Runs vgscan."""
+    global lvmDevicePresent
+
+    if flags.test or lvmDevicePresent == 0:
+        return
+
+    rc = lvmExec("vgscan", "-v")
+    if rc:
+        log.error("running vgscan failed: %s" %(rc,))
+#        lvmDevicePresent = 0
+
+def vgmknodes(volgroup=None):
+    # now make the device nodes
+    args = ["vgmknodes", "-v"]
+    if volgroup:
+        args.append(volgroup)
+    rc = lvmExec(*args)
+    if rc:
+        log.error("running vgmknodes failed: %s" %(rc,))
+#        lvmDevicePresent = 0
+
+def vgcheckactive(volgroup = None):
+    """Check if volume groups are active
+
+    volgroup - optional parameter to inquire about a specific volume group.
+    """
+    global lvmDevicePresent
+    if flags.test or lvmDevicePresent == 0:
+        return False
+
+    args = ["lvs", "--noheadings", "--units", "b", "--nosuffix",
+            "--separator", ":", "--options", "vg_name,lv_name,attr"]
+    for line in lvmCapture(*args):
+        try:
+            (vg, lv, attr) = line
+        except:
+            continue
+
+        log.info("lv %s/%s, attr is %s" %(vg, lv, attr))
+        if attr.find("a") == -1:
+            continue
+
+        if volgroup is None or volgroup == vg:
+            return True
+
+    return False
+
+def vgactivate(volgroup = None):
+    """Activate volume groups by running vgchange -ay.
+
+    volgroup - optional single volume group to activate
+    """
+    global lvmDevicePresent
+    if flags.test or lvmDevicePresent == 0:
+        return
+
+    args = ["vgchange", "-ay", "-v"]
+    if volgroup:
+        args.append(volgroup)
+    rc = lvmExec(*args)
+    if rc:
+        log.error("running vgchange failed: %s" %(rc,))
+#        lvmDevicePresent = 0
+    vgmknodes(volgroup)
+
+def vgdeactivate(volgroup = None):
+    """Deactivate volume groups by running vgchange -an.
+
+    volgroup - optional single volume group to deactivate
+    """
+    global lvmDevicePresent
+    if flags.test or lvmDevicePresent == 0:
+        return
+
+    args = ["vgchange", "-an", "-v"]
+    if volgroup:
+        args.append(volgroup)
+    rc = lvmExec(*args)
+    if rc:
+        log.error("running vgchange failed: %s" %(rc,))
+#        lvmDevicePresent = 0
+
+def lvcreate(lvname, vgname, size):
+    """Creates a new logical volume.
+
+    lvname - name of logical volume to create.
+    vgname - name of volume group lv will be in.
+    size - size of lv, in megabytes.
+    """
+    global lvmDevicePresent
+    if flags.test or lvmDevicePresent == 0:
+        return
+    writeForceConf()
+    vgscan()
+
+    args = ["lvcreate", "-v", "-L", "%dM" %(size,), "-n", lvname, "-An", vgname]
+    try:
+        rc = lvmExec(*args)
+    except:
+        rc = 1
+    if rc:
+        raise LVCreateError(vgname, lvname, size)
+    unlinkConf()
+
+def lvremove(lvname, vgname):
+    """Removes a logical volume.
+
+    lvname - name of logical volume to remove.
+    vgname - name of volume group lv is in.
+    """
+    global lvmDevicePresent
+    if flags.test or lvmDevicePresent == 0:
+        return
+
+    args = ["lvremove", "-f", "-v"]
+    dev = "/dev/%s/%s" %(vgname, lvname)
+    args.append(dev)
+
+    try:
+        rc = lvmExec(*args)
+    except:
+        rc = 1
+    if rc:
+        raise LVRemoveError(vgname, lvname)
+
+def lvresize(lvname, vgname, size):
+    global lvmDevicePresent
+    if flags.test or lvmDevicePresent == 0:
+        return
+
+    args = ["lvresize", "-An", "-L", "%dM" %(size,), "-v", "--force",
+            "/dev/%s/%s" %(vgname, lvname,)]
+
+    try:
+        rc = lvmExec(*args)
+    except:
+        rc = 1
+    if rc:
+        raise LVResizeError(vgname, lvname)
+
+
+def vgcreate(vgname, PESize, nodes):
+    """Creates a new volume group."
+
+    vgname - name of volume group to create.
+    PESize - Physical Extent size, in kilobytes.
+    nodes - LVM Physical Volumes on which to put the new VG.
+    """
+    global lvmDevicePresent
+    if flags.test or lvmDevicePresent == 0:
+        return
+
+    # rescan now that we've recreated pvs.  ugh.
+    writeForceConf()
+    vgscan()
+
+    args = ["vgcreate", "-v", "-An", "-s", "%sk" % (PESize,), vgname ]
+    args.extend(nodes)
+
+    try:
+        rc = lvmExec(*args)
+    except:
+        rc = 1
+    if rc:
+        raise VGCreateError(vgname, PESize, nodes)
+    unlinkConf()
+
+def vgremove(vgname):
+    """Removes a volume group.  Deactivates the volume group first
+
+    vgname - name of volume group.
+    """
+    global lvmDevicePresent
+    if flags.test or lvmDevicePresent == 0:
+        return
+
+    # find the Physical Volumes which make up this Volume Group, so we
+    # can prune and recreate them.
+    pvs = []
+    for pv in pvlist():
+        if pv[1] == vgname:
+            pvs.append(pv[0])
+
+    # we'll try to deactivate... if it fails, we'll probably fail on
+    # the removal too... but it's worth a shot
+    try:
+        vgdeactivate(vgname)
+    except:
+        pass
+
+    args = ["vgremove", "-v", vgname]
+
+    log.info(string.join(args, ' '))
+    try:
+        rc = lvmExec(*args)
+    except:
+        rc = 1
+    if rc:
+        raise VGRemoveError, vgname
+
+    # now iterate all the PVs we've just freed up, so we reclaim the metadata
+    # space.  This is an LVM bug, AFAICS.
+    for pvname in pvs:
+        args = ["pvremove", "-ff", "-y", "-v", pvname]
+
+        log.info(string.join(args, ' '))
+        try:
+            rc = lvmExec(*args)
+        except:
+            rc = 1
+        if rc:
+            raise PVRemoveError, pvname
+
+        args = ["pvcreate", "-ff", "-y", "-v", pvname]
+
+        log.info(string.join(args, ' '))
+        try:
+            rc = lvmExec(*args)
+        except:
+            rc = 1
+        if rc:
+            raise PVCreateError, pvname
+        wipeOtherMetadataFromPV(pvname)
+
+def pvcreate(node):
+    """Initializes a new Physical Volume."
+
+    node - path to device node on which to create the new PV."
+    """
+    global lvmDevicePresent
+    if flags.test or lvmDevicePresent == 0:
+        return
+
+    # rescan now that we've recreated pvs.  ugh.
+    writeForceConf()
+
+    args = ["pvcreate", "-ff", "-y", "-v", node ]
+
+    try:
+        rc = lvmExec(*args)
+    except:
+        rc = 1
+    if rc:
+        raise PVCreateError, node
+    unlinkConf()
+    wipeOtherMetadataFromPV(node)
+
+def lvlist():
+    global lvmDevicePresent
+    if lvmDevicePresent == 0:
+        return []
+
+    lvs = []
+    # field names for "options" are in LVM2.2.01.01/lib/report/columns.h
+    args = ["lvdisplay", "-C", "--noheadings", "--units", "b",
+            "--nosuffix", "--separator", ":", "--options",
+            "vg_name,lv_name,lv_size,origin"
+           ]
+    lvscanout = iutil.execWithCapture("lvm", args, stderr = "/dev/tty6")
+    for line in lvmCapture(*args):
+        try:
+            (vg, lv, size, origin) = line
+            size = long(math.floor(long(size) / (1024 * 1024)))
+            if origin == '':
+                origin = None
+        except:
+            continue
+
+        logmsg = "lv is %s/%s, size of %s" % (vg, lv, size)
+        if origin:
+            logmsg += ", snapshot from %s" % (origin,)
+        log.info(logmsg)
+        lvs.append( (vg, lv, size, origin) )
+
+    return lvs
+
+def pvlist():
+    global lvmDevicePresent
+    if lvmDevicePresent == 0:
+        return []
+
+    pvs = []
+    args = ["pvdisplay", "-C", "--noheadings", "--units", "b",
+            "--nosuffix", "--separator", ":", "--options",
+            "pv_name,vg_name,dev_size"
+           ]
+    for line in lvmCapture(*args):
+        try:
+            (dev, vg, size) = line
+            size = long(math.floor(long(size) / (1024 * 1024)))
+        except:
+            continue
+
+        if dev.startswith("/dev/dm-"):
+            from block import dm
+            try:
+                sb = os.stat(dev)
+                (major, minor) = (os.major(sb.st_rdev), os.minor(sb.st_rdev))
+                for map in dm.maps():
+                    if map.dev.major == major and map.dev.minor == minor:
+                        dev = "/dev/mapper/%s" % map.name
+                        break
+            except:
+                pass
+
+        log.info("pv is %s in vg %s, size is %s" %(dev, vg, size))
+        pvs.append( (dev, vg, size) )
+
+    return pvs
+
+def vglist():
+    global lvmDevicePresent
+    if lvmDevicePresent == 0:
+        return []
+
+    vgs = []
+    args = ["vgdisplay", "-C", "--noheadings", "--units", "b",
+            "--nosuffix", "--separator", ":", "--options",
+            "vg_name,vg_size,vg_extent_size,vg_free"
+           ]
+    for line in lvmCapture(*args):
+        try:
+            (vg, size, pesize, free) = line
+            size = long(math.floor(long(size) / (1024 * 1024)))
+            pesize = long(pesize)/1024
+            free = math.floor(long(free) / (1024 * 1024))
+        except:
+            continue
+        log.info("vg %s, size is %s, pesize is %s" %(vg, size, pesize))
+        vgs.append( (vg, size, pesize, free) )
+    return vgs
+
+def partialvgs():
+    global lvmDevicePresent
+    if lvmDevicePresent == 0:
+        return []
+
+    vgs = []
+    args = ["vgdisplay", "-C", "-P", "--noheadings", "--units", "b",
+            "--nosuffix", "--separator", ":"]
+    for line in lvmCapture(*args):
+        try:
+            (vg, numpv, numlv, numsn, attr, size, free) = line
+        except:
+            continue
+        if attr.find("p") != -1:
+            log.info("vg %s, attr is %s" %(vg, attr))
+            vgs.append(vg)
+
+    return vgs
+
+# FIXME: this is a hack.  we really need to have a --force option.
+def unlinkConf():
+    lvmroot = "/etc/lvm"
+    if os.path.exists("%s/lvm.conf" %(lvmroot,)):
+        os.unlink("%s/lvm.conf" %(lvmroot,))
+
+def writeForceConf():
+    """Write out an /etc/lvm/lvm.conf that doesn't do much (any?) filtering"""
+
+    lvmroot = "/etc/lvm"
+    try:
+        os.unlink("/etc/lvm/.cache")
+    except:
+        pass
+    if not os.path.isdir(lvmroot):
+        os.mkdir(lvmroot)
+
+    unlinkConf()
+
+    f = open("%s/lvm.conf" %(lvmroot,), "w+")
+    f.write("""
+# anaconda hacked lvm.conf to avoid filtering breaking things
+devices {
+  sysfs_scan = 0
+  md_component_detection = 1
+}
+""")
+
+# FIXME: another hack.  we need to wipe the raid metadata since pvcreate
+# doesn't
+def wipeOtherMetadataFromPV(node):
+    try:
+        isys.wipeRaidSB(node)
+    except Exception, e:
+        log.critical("error wiping raidsb from %s: %s", node, e)
+
+
+
+def getPossiblePhysicalExtents(floor=0):
+    """Returns a list of integers representing the possible values for
+       the physical extent of a volume group.  Value is in KB.
+
+       floor - size (in KB) of smallest PE we care about.
+    """
+
+    possiblePE = []
+    curpe = 8
+    while curpe <= 16384*1024:
+        if curpe >= floor:
+            possiblePE.append(curpe)
+        curpe = curpe * 2
+
+    return possiblePE
+
+def clampLVSizeRequest(size, pe, roundup=0):
+    """Given a size and a PE, returns the actual size of logical volumne.
+
+    size - size (in MB) of logical volume request
+    pe   - PE size (in KB)
+    roundup - round sizes up or not
+    """
+
+    if roundup:
+        func = math.ceil
+    else:
+        func = math.floor
+    return (long(func((size*1024L)/pe))*pe)/1024
+
+def clampPVSize(pvsize, pesize):
+    """Given a PV size and a PE, returns the usable space of the PV.
+    Takes into account both overhead of the physical volume and 'clamping'
+    to the PE size.
+
+    pvsize - size (in MB) of PV request
+    pesize - PE size (in KB)
+    """
+
+    # we want Kbytes as a float for our math
+    pvsize *= 1024.0
+    return long((math.floor(pvsize / pesize) * pesize) / 1024)
+
+def getMaxLVSize(pe):
+    """Given a PE size in KB, returns maximum size (in MB) of a logical volume.
+
+    pe - PE size in KB
+    """
+    # LVM2, size limited by number of sectors
+    return (16*1024*1024) #Max is 16TiB
+
+def safeLvmName(str):
+    tmp = string.strip(str)
+    tmp = tmp.replace("/", "_")
+    tmp = re.sub("[^0-9a-zA-Z._]", "", str)
+    tmp = tmp.lstrip("_")
+
+    return tmp
+
+def createSuggestedVGName(partitions, network):
+    """Given list of partition requests, come up with a reasonable VG name
+
+    partitions - list of requests
+    """
+
+    # try to create a volume group name incorporating the hostname
+    hn = network.hostname
+    if hn is not None and hn != '':
+        if hn == 'localhost' or hn == 'localhost.localdomain':
+            vgtemplate = "VolGroup"
+        elif hn.find('.') != -1:
+            hn = safeLvmName(hn)
+            vgtemplate = "vg_%s" % (hn.split('.')[0].lower(),)
+        else:
+            hn = safeLvmName(hn)
+            vgtemplate = "vg_%s" % (hn.lower(),)
+    else:
+        vgtemplate = "VolGroup"
+
+    if not partitions.isVolumeGroupNameInUse(vgtemplate):
+        return vgtemplate
+    else:
+        i = 0
+        while 1:
+            tmpname = "%s%02d" % (vgtemplate, i,)
+            if not partitions.isVolumeGroupNameInUse(tmpname):
+                break
+
+            i += 1
+            if i > 99:
+                tmpname = ""
+
+        return tmpname
+
+def createSuggestedLVName(logreqs):
+    """Given list of LV requests, come up with a reasonable LV name
+
+    partitions - list of LV requests for this VG
+    """
+
+    i = 0
+
+    lnames = []
+    for lv in logreqs:
+        lnames.append(lv.logicalVolumeName)
+
+    while 1:
+        tmpname = "LogVol%02d" % (i,)
+        if (logreqs is None) or (tmpname not in lnames):
+            break
+
+        i += 1
+        if i > 99:
+            tmpname = ""
+
+    return tmpname
+
+def getVGUsedSpace(vgreq, requests, diskset):
+    vgused = 0
+    for request in requests.requests:
+        if request.type == REQUEST_LV and request.volumeGroup == vgreq.uniqueID:
+            size = int(request.getActualSize(requests, diskset))
+            vgused = vgused + size
+
+
+    return vgused
+
+def getVGFreeSpace(vgreq, requests, diskset):
+    used = getVGUsedSpace(vgreq, requests, diskset)
+    log.debug("used space is %s" % (used,))
+
+    total = vgreq.getActualSize(requests, diskset)
+    log.debug("actual space is %s" % (total,))
+    return total - used
index 97fd52c77381e08414435837ec288f8360b2787d..3bb39be4fec6c848d15e79a82e561c639aeab206 100644 (file)
@@ -16,7 +16,7 @@
 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 #
 
-import inutil
+import iutil
 import isys
 import os
 import sys
@@ -94,7 +94,7 @@ def setupTimezone(pomona):
         args.append("-l")
 
     try:
-        inutil.execWithRedirect("/sbin/hwclock", args, stdin = None,
+        iutil.execWithRedirect("/sbin/hwclock", args, stdin = None,
                                 stdout = "/dev/tty5", stderr = "/dev/tty5")
     except RuntimeError:
         log.error("Failed to set clock")
index f300ace1a629bf61a53eb0f1e67b171628a05ac3..45aacdc0907f6c7f6942ca48f44a367f1ea6baed 100644 (file)
@@ -34,7 +34,7 @@ log = logging.getLogger("pomona")
 import urlparse
 urlparse.uses_fragment.append('media')
 
-import inutil
+import iutil
 import isys
 import pyfire
 
index 80d6ded09dab916ed7a2576eebf8a75a94aac0b0..2d2833f0ac13ad7514d1382f7ebe46927410ed4d 100644 (file)
@@ -1,33 +1,86 @@
 #
 # partIntfHelpers.py: partitioning interface helper functions
 #
-# Matt Wilson <msw@redhat.com>
-# Jeremy Katz <katzj@redhat.com>
-# Mike Fulbright <msf@redhat.com>
-# Harald Hoyer <harald@redhat.de>
+# Copyright (C) 2002  Red Hat, Inc.  All rights reserved.
 #
-# Copyright 2002 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): Matt Wilson <msw@redhat.com>
+#            Jeremy Katz <katzj@redhat.com>
+#            Mike Fulbright <msf@redhat.com>
+#            Harald Hoyer <harald@redhat.de>
+#
+
 """Helper functions shared between partitioning interfaces."""
 
 import string
-
 from constants import *
 import partedUtils
 import parted
 import fsset
+import iutil
 import partRequests
 
 import gettext
 _ = lambda x: gettext.ldgettext("pomona", x)
 
+def sanityCheckVolumeGroupName(volname):
+    """Make sure that the volume group name doesn't contain invalid chars."""
+    badNames = ['lvm', 'root', '.', '..' ]
+
+    if not volname:
+        return _("Please enter a volume group name.")
+
+    # ripped the value for this out of linux/include/lvm.h
+    if len(volname) > 128:
+        return _("Volume Group Names must be less than 128 characters")
+
+    if volname in badNames:
+        return _("Error - the volume group name %s is not valid." % (volname,))
+
+    for i in range(0, len(volname)):
+        rc = string.find(string.letters + string.digits + '.' + '_', volname[i])
+        if rc == -1:
+            return _("Error - the volume group name contains illegal "
+                     "characters or spaces.  Acceptable characters "
+                     "are letters, digits, '.' or '_'.")
+    return None
+
+def sanityCheckLogicalVolumeName(logvolname):
+    """Make sure that the logical volume name doesn't contain invalid chars."""
+    badNames = ['group', '.', '..' ]
+
+    if not logvolname:
+        return _("Please enter a logical volume name.")
+
+    # ripped the value for this out of linux/include/lvm.h
+    if len(logvolname) > 128:
+        return _("Logical Volume Names must be less than 128 characters")
+
+
+    if logvolname in badNames:
+        return _("Error - the logical volume name %s is not "
+                 "valid." % (logvolname,))
+
+    for i in range(0, len(logvolname)):
+        rc = string.find(string.letters + string.digits + '.' + '_', logvolname[i])
+        if rc == -1:
+            return _("Error - the logical volume name contains illegal "
+                     "characters or spaces.  Acceptable characters "
+                     "are letters, digits, '.' or '_'.")
+    return None
+
 def sanityCheckMountPoint(mntpt, fstype, preexisting, format):
     """Sanity check that the mountpoint is valid.
 
@@ -60,9 +113,28 @@ def sanityCheckMountPoint(mntpt, fstype, preexisting, format):
             return None
 
 def isNotChangable(request, requestlist):
+    if request:
+        if requestlist.isRaidMember(request):
+            parentreq = requestlist.getRaidMemberParent(request)
+            if parentreq.raidminor is not None:
+                return _("This partition is part of "
+                         "the RAID device /dev/md%s.") % (parentreq.raidminor,)
+            else:
+                return _("This partition is part of a RAID device.")
+
+        if requestlist.isLVMVolumeGroupMember(request):
+            parentreq = requestlist.getLVMVolumeGroupMemberParent(request)
+            if parentreq.volumeGroupName is not None:
+                return _("This partition is part of the "
+                         "LVM volume group '%s'.") % (parentreq.volumeGroupName,)
+            else:
+                return _("This partition is part of a LVM volume group.")
+
     return None
 
-def doDeletePartitionByRequest(intf, requestlist, partition, confirm=1, quiet=0):
+
+def doDeletePartitionByRequest(intf, requestlist, partition,
+                               confirm=1, quiet=0):
     """Delete a partition from the request list.
 
     intf is the interface
@@ -76,11 +148,20 @@ def doDeletePartitionByRequest(intf, requestlist, partition, confirm=1, quiet=0)
                            custom_icon="error")
         return 0
 
-    if partition.type & parted.PARTITION_FREESPACE:
+    if type(partition) == type("RAID"):
+        device = partition
+    elif partition.type & parted.PARTITION_FREESPACE:
         intf.messageWindow(_("Unable To Delete"),
                            _("You cannot delete free space."),
                            custom_icon="error")
         return 0
+    elif partition.type & parted.PARTITION_PROTECTED:
+        # LDL formatted DASDs always have one partition, you'd have to reformat the
+        # DASD in CDL mode to get rid of it
+        intf.messageWindow(_("Unable To Delete"),
+                           _("You cannot delete a partition of a LDL formatted DASD."),
+                           custom_icon="error")
+        return 0
     else:
         device = partedUtils.get_partition_name(partition)
 
@@ -90,12 +171,15 @@ def doDeletePartitionByRequest(intf, requestlist, partition, confirm=1, quiet=0)
             intf.messageWindow(_("Unable To Delete"),
                                _("You cannot delete this "
                                  "partition, as it is an extended partition "
-                                 "which contains %s")
-                               % (ret), custom_icon="error")
+                                 "which contains %s") %(ret),
+                               custom_icon="error")
         return 0
 
     # see if device is in our partition requests, remove
-    request = requestlist.getRequestByDeviceName(device)
+    if type(partition) == type("RAID"):
+        request = requestlist.getRequestByID(device)
+    else:
+        request = requestlist.getRequestByDeviceName(device)
 
     if request:
         state = isNotChangable(request, requestlist)
@@ -111,7 +195,7 @@ def doDeletePartitionByRequest(intf, requestlist, partition, confirm=1, quiet=0)
                 intf.messageWindow(_("Unable To Delete"),
                                    _("You cannot delete this partition:\n\n") + state,
                                    custom_icon="error")
-            return (None, None)
+            return 0
 
         if confirm and not confirmDeleteRequest(intf, request):
             return 0
@@ -124,10 +208,21 @@ def doDeletePartitionByRequest(intf, requestlist, partition, confirm=1, quiet=0)
                 if partition.type & parted.PARTITION_EXTENDED:
                     requestlist.deleteAllLogicalPartitions(partition)
 
-                delete = partRequests.DeleteSpec(drive, partition.geom.start, partition.geom.end)
+                delete = partRequests.DeleteSpec(drive, partition.geom.start,
+                                                 partition.geom.end)
+                requestlist.addDelete(delete)
+            elif isinstance(request, partRequests.LogicalVolumeRequestSpec):
+                vgreq = requestlist.getRequestByID(request.volumeGroup)
+                delete = partRequests.DeleteLogicalVolumeSpec(request.logicalVolumeName,
+                                                              vgreq.volumeGroupName)
                 requestlist.addDelete(delete)
+            elif isinstance(request, partRequests.VolumeGroupRequestSpec):
+                delete = partRequests.DeleteVolumeGroupSpec(request.volumeGroupName)
+                requestlist.addDelete(delete)
+            # FIXME: do we need to do anything with preexisting raids?
 
         # now remove the request
+        requestlist.deleteDependentRequests(request)
         requestlist.removeRequest(request)
     else: # is this a extended partition we made?
         if partition.type & parted.PARTITION_EXTENDED:
@@ -140,7 +235,7 @@ def doDeletePartitionByRequest(intf, requestlist, partition, confirm=1, quiet=0)
     return 1
 
 def doDeletePartitionsByDevice(intf, requestlist, diskset, device,
-                        confirm=1, quiet=0):
+                               confirm=1, quiet=0):
     """ Remove all partitions currently on device """
     if confirm:
         rc = intf.messageWindow(_("Confirm Delete"),
@@ -148,12 +243,13 @@ def doDeletePartitionsByDevice(intf, requestlist, diskset, device,
                                   "the device '/dev/%s'.") % (device,),
                                 type="custom", custom_icon="warning",
                                 custom_buttons=[_("Cancel"), _("_Delete")])
+
         if not rc:
             return
 
-        requests = requestlist.getRequestsByDevice(diskset, device)
-        if not requests:
-            return
+    requests = requestlist.getRequestsByDevice(diskset, device)
+    if not requests:
+        return
 
     # get list of unique IDs of these requests
     reqIDs = []
@@ -170,9 +266,9 @@ def doDeletePartitionsByDevice(intf, requestlist, diskset, device,
             if req is None:
                 continue
             part = partedUtils.get_partition_by_name(diskset.disks, req.device)
-            rc = doDeletePartitionByRequest(intf, requestlist, part, confirm=0, quiet=1)
-            # not sure why it returns both '0' and '(None, None)' on failure
-            if not rc or rc == (None, None):
+            rc = doDeletePartitionByRequest(intf, requestlist, part,
+                                            confirm=0, quiet=1)
+            if not rc:
                 pass
         except:
             pass
@@ -189,9 +285,9 @@ def doDeletePartitionsByDevice(intf, requestlist, diskset, device,
                 continue
             leftIDs.append(req.uniqueID)
 
-    for id in leftIDs:
-        req = requestlist.getRequestByID(id)
-        notdeleted.append(req)
+        for id in leftIDs:
+            req = requestlist.getRequestByID(id)
+            notdeleted.append(req)
 
 
     # see if we need to report any failures - some were because we removed
@@ -202,14 +298,15 @@ def doDeletePartitionsByDevice(intf, requestlist, diskset, device,
         if newreq:
             outlist = outlist + "\t/dev/%s\n" % (newreq.device,)
 
-            if outlist != "" and not quiet:
-                intf.messageWindow(_("Notice"),
-                                   _("The following partitions were not deleted "
-                                     "because they are in use:\n\n%s") % outlist,
-                                   custom_icon="warning")
+    if outlist != "" and not quiet:
+        intf.messageWindow(_("Notice"),
+                           _("The following partitions were not deleted "
+                             "because they are in use:\n\n%s") % outlist,
+                           custom_icon="warning")
 
     return 1
 
+
 def doEditPartitionByRequest(intf, requestlist, part):
     """Edit a partition from the request list.
 
@@ -221,13 +318,36 @@ def doEditPartitionByRequest(intf, requestlist, part):
     if part == None:
         intf.messageWindow(_("Unable To Edit"),
                            _("You must select a partition to edit"), custom_icon="error")
+
         return (None, None)
 
-    if part.type & parted.PARTITION_FREESPACE:
+    if type(part) == type("RAID"):
+
+        # see if device is in our partition requests, remove
+        request = requestlist.getRequestByID(int(part))
+
+        if request:
+            state = isNotChangable(request, requestlist)
+            if state is not None:
+                intf.messageWindow(_("Unable To Edit"), _("You cannot edit this partition:\n\n") + state,
+                                   custom_icon="error")
+                return (None, None)
+
+        if request.type == REQUEST_RAID:
+            return ("RAID", request)
+        elif request.type == REQUEST_VG:
+            return ("LVMVG", request)
+        elif request.type == REQUEST_LV:
+            return ("LVMLV", request)
+        else:
+            return (None, None)
+    elif part.type & parted.PARTITION_FREESPACE:
         request = partRequests.PartitionSpec(fsset.fileSystemTypeGetDefault(),
-                                             start = partedUtils.start_sector_to_cyl(part.geom.dev, part.geom.start),
-                                             end = partedUtils.end_sector_to_cyl(part.geom.dev, part.geom.end),
-                                             drive = [ partedUtils.get_partition_drive(part) ])
+            start = partedUtils.start_sector_to_cyl(part.geom.dev,
+                                                    part.geom.start),
+            end = partedUtils.end_sector_to_cyl(part.geom.dev,
+                                                part.geom.end),
+            drive = [ partedUtils.get_partition_drive(part) ])
 
         return ("NEW", request)
     elif part.type & parted.PARTITION_EXTENDED:
@@ -239,7 +359,7 @@ def doEditPartitionByRequest(intf, requestlist, part):
                            _("You cannot edit this "
                              "partition, as it is an extended partition "
                              "which contains %s") %(ret), custom_icon="error")
-        return 0
+        return (None, None)
 
     name = partedUtils.get_partition_name(part)
     request = requestlist.getRequestByDeviceName(name)
@@ -247,42 +367,46 @@ def doEditPartitionByRequest(intf, requestlist, part):
         state = isNotChangable(request, requestlist)
         if state is not None:
             intf.messageWindow(_("Unable To Edit"),
-                               _("You cannot edit this partition:\n\n") + state,
-                               custom_icon="error")
+                               _("You cannot edit this partition:\n\n") + state, custom_icon="error")
             return (None, None)
 
         return ("PARTITION", request)
     else: # shouldn't ever happen
         raise ValueError, ("Trying to edit non-existent partition %s"
-                % (partedUtils.get_partition_name(part)))
+                           % (partedUtils.get_partition_name(part)))
 
 
-def checkForSwapNoMatch(pomona):
+def checkForSwapNoMatch(anaconda):
     """Check for any partitions of type 0x82 which don't have a swap fs."""
-    for request in pomona.id.partitions.requests:
+    for request in anaconda.id.partitions.requests:
         if not request.device or not request.fstype:
             continue
 
-        part = partedUtils.get_partition_by_name(pomona.id.diskset.disks, request.device)
-
+        part = partedUtils.get_partition_by_name(anaconda.id.diskset.disks,
+                                                 request.device)
         if (part and (not part.type & parted.PARTITION_FREESPACE)
-                                         and (part.native_type == 0x82)
-                                         and (request.fstype and request.fstype.getName() != "swap")
-                                         and (not request.format)):
-            rc = pomona.intf.messageWindow(_("Format as Swap?"),
-                                           _("/dev/%s has a partition type of 0x82 "
-                                             "(Linux swap) but does not appear to "
-                                             "be formatted as a Linux swap "
-                                             "partition.\n\n"
-                                             "Would you like to format this "
-                                             "partition as a swap partition?")
-                                           % (request.device), type = "yesno",
-                                             custom_icon="question")
+            and (part.native_type == 0x82)
+            and (request.fstype and request.fstype.getName() != "swap")
+            and (not request.format)):
+            rc = anaconda.intf.messageWindow(_("Format as Swap?"),
+                                    _("/dev/%s has a partition type of 0x82 "
+                                      "(Linux swap) but does not appear to "
+                                      "be formatted as a Linux swap "
+                                      "partition.\n\n"
+                                      "Would you like to format this "
+                                      "partition as a swap partition?")
+                                    % (request.device), type = "yesno",
+                                    custom_icon="question")
             if rc == 1:
                 request.format = 1
                 request.fstype = fsset.fileSystemTypeGet("swap")
-                partedUtils.set_partition_file_system_type(part, request.fstype)
+                if request.fstype.getName() == "software RAID":
+                    part.set_flag(parted.PARTITION_RAID, 1)
+                else:
+                    part.set_flag(parted.PARTITION_RAID, 0)
 
+                partedUtils.set_partition_file_system_type(part,
+                                                           request.fstype)
 
 def mustHaveSelectedDrive(intf):
     txt =_("You need to select at least one hard drive to install %s.") % (name,)
@@ -296,11 +420,9 @@ def queryNoFormatPreExisting(intf):
             "to make sure files from a previous operating system installation "
             "do not cause problems with this installation of Linux. "
             "However, if this partition contains files that you need "
-            "to keep, such as home directories, then  "
+            "to keep, such as home directories, then "
             "continue without formatting this partition.")
-    rc = intf.messageWindow(_("Format?"), txt, type = "custom",
-                            custom_buttons=[_("_Modify Partition"), _("Do _Not Format")],
-                            custom_icon="warning")
+    rc = intf.messageWindow(_("Format?"), txt, type = "custom", custom_buttons=[_("_Modify Partition"), _("Do _Not Format")], custom_icon="warning")
     return rc
 
 def partitionSanityErrors(intf, errors):
@@ -324,12 +446,12 @@ def partitionSanityWarnings(intf, warnings):
     if warnings:
         warningstr = string.join(warnings, "\n\n")
         rc = intf.messageWindow(_("Partitioning Warning"),
-                                _("The following warnings exist with "
-                                  "your requested partition scheme.\n\n%s"
-                                  "\n\nWould you like to continue with "
-                                  "your requested partitioning "
-                                  "scheme?") % (warningstr),
-                                type="yesno", custom_icon="warning")
+                                     _("The following warnings exist with "
+                                       "your requested partition scheme.\n\n%s"
+                                       "\n\nWould you like to continue with "
+                                       "your requested partitioning "
+                                       "scheme?") % (warningstr),
+                                     type="yesno", custom_icon="warning")
     return rc
 
 
@@ -337,8 +459,10 @@ def partitionPreExistFormatWarnings(intf, warnings):
     """Double check that preexistings being formatted are fine."""
     rc = 1
     if warnings:
+
         labelstr1 = _("The following pre-existing partitions have been "
                       "selected to be formatted, destroying all data.")
+
         labelstr2 = _("Select 'Yes' to continue and format these "
                       "partitions, or 'No' to go back and change these "
                       "settings.")
@@ -362,7 +486,7 @@ def getPreExistFormatWarnings(partitions, diskset):
 
     rc = []
     for dev in devs:
-        request = partitions.getRequestByID("%s" % (dev,))
+        request = partitions.getRequestByID(dev)
         if request.format:
             if request.fstype.isMountable():
                 mntpt = request.mountpoint
@@ -371,6 +495,15 @@ def getPreExistFormatWarnings(partitions, diskset):
 
             if isinstance(request, partRequests.PartitionSpec):
                 dev = request.device
+            elif isinstance(request, partRequests.RaidRequestSpec):
+                dev = "md%s" %(request.raidminor,)
+            elif isinstance(request, partRequests.VolumeGroupRequestSpec):
+                dev = request.volumeGroupName
+            elif isinstance(request, partRequests.LogicalVolumeRequestSpec):
+                vgreq = partitions.getRequestByID(request.volumeGroup)
+                dev = "%s/%s" %(vgreq.volumeGroupName,
+                                request.logicalVolumeName)
+
             rc.append((dev, request.fstype.getName(), mntpt))
 
     if len(rc) == 0:
@@ -383,13 +516,25 @@ def confirmDeleteRequest(intf, request):
     if not request:
         return
 
-    if request.device:
-        errmsg = _("You are about to delete the /dev/%s partition.") % (request.device,)
+    if request.type == REQUEST_VG:
+        errmsg = (_("You are about to delete the volume group \"%s\"."
+                    "\n\nALL logical volumes in this volume group "
+                    "will be lost!") % (request.volumeGroupName,))
+    elif request.type == REQUEST_LV:
+        errmsg = (_("You are about to delete the logical volume \"%s\".")
+                  % (request.logicalVolumeName,))
+    elif request.type == REQUEST_RAID:
+        errmsg = _("You are about to delete a RAID device.")
     else:
-        errmsg = _("The partition you selected will be deleted.")
+        if request.device:
+            errmsg = _("You are about to delete the /dev/%s partition.") % (request.device,)
+        else:
+            # XXX can this ever happen?
+            errmsg = _("The partition you selected will be deleted.")
 
     rc = intf.messageWindow(_("Confirm Delete"), errmsg, type="custom",
-            custom_buttons=[_("Cancel"), _("_Delete")], custom_icon="question")
+                                custom_buttons=[_("Cancel"), _("_Delete")],
+                            custom_icon="question")
 
     return rc
 
index 437e9ec4751a496781b043f7d1e1a89d6b98b581..73ec628eb8e40273362c8cd02f0a0ae104336f79 100644 (file)
@@ -1,41 +1,53 @@
 #
 # partRequests.py: partition request objects and management thereof
 #
-# Matt Wilson <msw@redhat.com>
-# Jeremy Katz <katzj@redhat.com>
-# Mike Fulbright <msf@redhat.com>
-# Harald Hoyer <harald@redhat.de>
+# Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007  Red Hat, Inc.
+# All rights reserved.
 #
-# Copyright 2002-2005 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): Matt Wilson <msw@redhat.com>
+#            Jeremy Katz <katzj@redhat.com>
+#            Mike Fulbright <msf@redhat.com>
+#            Harald Hoyer <harald@redhat.de>
+#
+
 """Partition request objects and management thereof."""
 
 import parted
-import inutil
+import iutil
 import string
 import os, sys, math
 
 from constants import *
+from flags import *
+
+import gettext
+_ = lambda x: gettext.ldgettext("pomona", x)
 
 import fsset
+import raid
+import lvm
 import partedUtils
 import partIntfHelpers
 
-import gettext
-_ = lambda x: gettext.ldgettext("pomona", x)
-
 import logging
 log = logging.getLogger("pomona")
 
 class DeleteSpec:
     """Defines a preexisting partition which is intended to be removed."""
+
     def __init__(self, drive, start, end):
         """Initializes a DeleteSpec.
 
@@ -49,14 +61,75 @@ class DeleteSpec:
         self.end = end
 
     def __str__(self):
-        return "drive: %s  start: %s  end: %s" % (self.drive, self.start, self.end)
+        return "drive: %s  start: %s  end: %s" %(self.drive, self.start,
+                                                 self.end)
+
+class DeleteLogicalVolumeSpec:
+    """Defines a preexisting logical volume which is intended to be removed."""
+
+    def __init__(self, name, vg):
+        """Initializes a DeleteLogicalVolumeSpec.
+
+        name is the name of the lv
+        vg is the name of the volume group
+        """
+
+        self.name = name
+        self.vg = vg
+        self.deleted = 0
+
+    def __str__(self):
+        return "lvname: %s  vgname: %s" %(self.name, self.vg)
+
+    def beenDeleted(self):
+        return self.deleted
+
+    def setDeleted(self, val):
+        self.deleted = val
+
+class DeleteVolumeGroupSpec:
+    """Defines a preexisting volume group which is intended to be removed."""
+
+    def __init__(self, name):
+        """Initializes a DeleteVolumeGroupSpec
+
+        name is the name of the volume group
+        """
+
+        self.name = name
+        self.deleted = 0
+
+    def __str__(self):
+        return "vgname: %s" %(self.name,)
+
+    def beenDeleted(self):
+        return self.deleted
+
+    def setDeleted(self, val):
+        self.deleted = val
+
+class DeleteRAIDSpec:
+    """Defines a preexisting RAID device which is intended to be removed."""
+
+    def __init__(self, minor):
+        """Initializes a DeleteRAIDSpec.
+
+        minor is the minor of the RAID device being removed
+        """
+
+        self.minor = minor
+
+    def __str__(self):
+        return "minor: %s" %(self.minor,)
 
 class RequestSpec:
     """Generic Request specification."""
     def __init__(self, fstype, size = None, mountpoint = None, format = None,
-                 badblocks = None, preexist = 0, fslabel = None,
-                 migrate = None, origfstype = None, bytesPerInode = 4096):
+                 preexist = 0, fslabel = None,
+                 migrate = None, origfstype = None,
+                 fsprofile = None):
         """Create a generic RequestSpec.
+
         This should probably never be externally used.
         """
 
@@ -64,17 +137,12 @@ class RequestSpec:
         self.mountpoint = mountpoint
         self.size = size
         self.format = format
-        self.badblocks = badblocks
 
         self.migrate = migrate
         self.origfstype = origfstype
         self.fslabel = fslabel
         self.fsopts = None
-
-        if bytesPerInode == None:
-            self.bytesPerInode = 4096
-        else:
-            self.bytesPerInode = bytesPerInode
+        self.fsprofile = fsprofile
 
         self.device = None
         """what we currently think the device is"""
@@ -94,6 +162,15 @@ class RequestSpec:
         self.dev = None
         """A Device() as defined in fsset.py to correspond to this request."""
 
+        self.encryption = None
+        """An optional LUKSDevice() describing block device encryption."""
+
+        self.targetSize = None
+        """Size to resize to"""
+
+        self.resizable = False
+        """Is this a request that can be resized?"""
+
     def __str__(self):
         if self.fstype:
             fsname = self.fstype.getName()
@@ -101,23 +178,40 @@ class RequestSpec:
             fsname = "None"
 
         str = ("Generic Request -- mountpoint: %(mount)s  uniqueID: %(id)s\n"
-               "  type: %(fstype)s  format: %(format)s  badblocks: %(bb)s\n"
+               "  type: %(fstype)s  format: %(format)s\n"
                "  device: %(dev)s  migrate: %(migrate)s  fslabel: %(fslabel)s\n"
-               "  bytesPerInode:  %(bytesPerInode)s  options: '%(fsopts)s'"
-               % {"mount": self.mountpoint, "id": self.uniqueID,
-                  "fstype": fsname, "format": self.format, "bb": self.badblocks,
-                  "dev": self.device, "migrate": self.migrate,
-                  "fslabel": self.fslabel, "bytesPerInode": self.bytesPerInode,
-                  "fsopts": self.fsopts})
+               "  options: '%(fsopts)s'"
+               "  fsprofile: %(fsprofile)s" %
+               {"mount": self.mountpoint, "id": self.uniqueID,
+                "fstype": fsname, "format": self.format,
+                "dev": self.device, "migrate": self.migrate,
+                "fslabel": self.fslabel,
+                "fsopts": self.fsopts, "fsprofile": self.fsprofile})
         return str
 
     def getActualSize(self, partitions, diskset):
         """Return the actual size allocated for the request in megabytes."""
+
         sys.stderr.write("WARNING: Abstract RequestSpec.getActualSize() called\n")
+        import traceback
+        traceback.print_stack()
 
     def getDevice(self, partitions):
         """Return a device to solidify."""
+
         sys.stderr.write("WARNING: Abstract RequestSpec.getDevice() called\n")
+        import traceback
+        traceback.print_stack()
+
+    def isResizable(self, partitions):
+        if self.isEncrypted(partitions): # FIXME: can't resize crypted devs yet
+            return False
+        return self.resizable and self.fstype is not None and self.fstype.isResizable()
+
+    def isEncrypted(self, partitions, parentOnly = False):
+        if self.encryption:
+            return True
+        return False
 
     def toEntry(self, partitions):
         """Turn a request into a fsset entry and return the entry."""
@@ -133,20 +227,20 @@ class RequestSpec:
 
         entry = fsset.FileSystemSetEntry(device, mountpoint, self.fstype,
                                          origfsystem=self.origfstype,
-                                         bytesPerInode=self.bytesPerInode,
-                                         options=self.fsopts)
+                                         options=self.fsopts,
+                                         fsprofile=self.fsprofile)
         if self.format:
             entry.setFormat(self.format)
 
         if self.migrate:
             entry.setMigrate(self.migrate)
 
-        if self.badblocks:
-            entry.setBadblocks(self.badblocks)
-
         if self.fslabel:
             entry.setLabel(self.fslabel)
 
+        if self.targetSize and self.fstype.isResizable():
+            entry.setResizeTarget(self.targetSize, self.size)
+
         return entry
 
     def setProtected(self, val):
@@ -169,7 +263,7 @@ class RequestSpec:
                            '/usr/share', '/usr/lib' )
 
         # these are symlinks so you cant make them mount points
-        otherexcept = ('/var/mail', '/usr/tmp')
+        otherexcept = ('/var/mail', '/usr/bin/X11', '/usr/lib/X11', '/usr/tmp')
 
         if not self.mountpoint:
             return None
@@ -177,9 +271,17 @@ 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 "
+                return _("This mount point is invalid.  The %s directory must "
                          "be on the / file system.") % (self.mountpoint,)
             elif self.mountpoint in otherexcept:
                 return _("The mount point %s cannot be used.  It must "
@@ -187,9 +289,11 @@ class RequestSpec:
                          "operation.  Please select a different "
                          "mount point.") % (self.mountpoint,)
 
-            if not self.fstype.isLinuxNativeFS():
-                if self.mountpoint in mustbeonlinuxfs:
-                    return _("This mount point must be on a linux file system.")
+            return None
+
+        if not self.fstype.isLinuxNativeFS():
+            if self.mountpoint in mustbeonlinuxfs:
+                return _("This mount point must be on a linux file system.")
 
         return None
 
@@ -208,10 +312,11 @@ class RequestSpec:
                     continue
 
                 if request.mountpoint == mntpt:
-                    if (not self.uniqueID or request.uniqueID != self.uniqueID):
+                    if (not self.uniqueID or
+                        request.uniqueID != self.uniqueID):
                         return _("The mount point \"%s\" is already in use, "
                                  "please choose a different mount point."
-                               (mntpt))
+                                 %(mntpt))
         return None
 
     def doSizeSanityCheck(self):
@@ -227,6 +332,7 @@ class RequestSpec:
                       "exceeds the maximum size of %10.2f MB.")
                     % (self.fstype.getName(), self.size,
                        self.fstype.getMaxSizeMB()))
+
         return None
 
     # set skipMntPtExistCheck to non-zero if you want to handle this
@@ -258,10 +364,11 @@ class RequestSpec:
 
         return None
 
+
     def formatByDefault(self):
         """Return whether or not the request should be formatted by default."""
         def inExceptionList(mntpt):
-            exceptlist = [ '/home' ]
+            exceptlist = ['/home', '/usr/local', '/opt', '/var/www']
             for q in exceptlist:
                 if os.path.commonprefix([mntpt, q]) == q:
                     return 1
@@ -290,6 +397,8 @@ class RequestSpec:
                         return 0
                     else:
                         return 1
+
+            return 0
         else:
             if self.fstype.getName() == "swap":
                 return 1
@@ -302,11 +411,12 @@ class RequestSpec:
 class PartitionSpec(RequestSpec):
     """Object to define a requested partition."""
 
+    # XXX eep, still a few too many options but a lot better
     def __init__(self, fstype, size = None, mountpoint = None,
                  preexist = 0, migrate = None, grow = 0, maxSizeMB = None,
                  start = None, end = None, drive = None, primary = None,
-                 format = None, multidrive = None, bytesPerInode = 4096,
-                 fslabel = None):
+                 format = None, multidrive = None,
+                 fslabel = None, fsprofile=None):
         """Create a new PartitionSpec object.
 
         fstype is the fsset filesystem type.
@@ -323,8 +433,8 @@ class PartitionSpec(RequestSpec):
         migrate is whether or not the partition should be migrated.
         multidrive specifies if this is a request that should be replicated
             across _all_ of the drives in drive
-        bytesPerInode is the size of the inodes on the filesystem.
         fslabel is the label to give to the filesystem.
+        fsprofile is the usage profile for the filesystem.
         """
 
         # if it's preexisting, the original fstype should be set
@@ -336,8 +446,8 @@ class PartitionSpec(RequestSpec):
         RequestSpec.__init__(self, fstype = fstype, size = size,
                              mountpoint = mountpoint, format = format,
                              preexist = preexist, migrate = None,
-                             origfstype = origfs, bytesPerInode = bytesPerInode,
-                             fslabel = fslabel)
+                             origfstype = origfs,
+                             fslabel = fslabel, fsprofile = fsprofile)
         self.type = REQUEST_NEW
 
         self.grow = grow
@@ -354,6 +464,7 @@ class PartitionSpec(RequestSpec):
         self.currentDrive = None
         """Drive that this request will currently end up on."""
 
+
     def __str__(self):
         if self.fstype:
             fsname = self.fstype.getName()
@@ -370,33 +481,46 @@ class PartitionSpec(RequestSpec):
         else:
             pre = "Existing"
 
+        if self.encryption is None:
+            crypto = "None"
+        else:
+            crypto = self.encryption.getScheme()
+
         str = ("%(n)s Part Request -- mountpoint: %(mount)s uniqueID: %(id)s\n"
-               "  type: %(fstype)s  format: %(format)s  badblocks: %(bb)s\n"
+               "  type: %(fstype)s  format: %(format)s \n"
                "  device: %(dev)s drive: %(drive)s  primary: %(primary)s\n"
                "  size: %(size)s  grow: %(grow)s  maxsize: %(max)s\n"
                "  start: %(start)s  end: %(end)s  migrate: %(migrate)s  "
                "  fslabel: %(fslabel)s  origfstype: %(origfs)s\n"
-               "  bytesPerInode: %(bytesPerInode)s  options: '%(fsopts)s'"
-               % {"n": pre, "mount": self.mountpoint, "id": self.uniqueID,
-                  "fstype": fsname, "format": self.format, "dev": self.device,
-                  "drive": self.drive, "primary": self.primary,
-                  "size": self.size, "grow": self.grow, "max": self.maxSizeMB,
-                  "start": self.start, "end": self.end, "bb": self.badblocks,
-                  "migrate": self.migrate, "fslabel": self.fslabel,
-                  "origfs": oldfs, "bytesPerInode": self.bytesPerInode,
-                  "fsopts": self.fsopts})
+               "  options: '%(fsopts)s'\n"
+               "  fsprofile: %(fsprofile)s  encryption: %(encryption)s" %
+               {"n": pre, "mount": self.mountpoint, "id": self.uniqueID,
+                "fstype": fsname, "format": self.format, "dev": self.device,
+                "drive": self.drive, "primary": self.primary,
+                "size": self.size, "grow": self.grow, "max": self.maxSizeMB,
+                "start": self.start, "end": self.end,
+                "migrate": self.migrate, "fslabel": self.fslabel,
+                "origfs": oldfs,
+                "fsopts": self.fsopts, "fsprofile": self.fsprofile,
+                "encryption": crypto})
         return str
 
 
     def getDevice(self, partitions):
         """Return a device to solidify."""
-        self.dev = fsset.PartitionDevice(self.device)
+        if self.dev:
+            return self.dev
+
+        self.dev = fsset.PartitionDevice(self.device,
+                                         encryption = self.encryption)
+
         return self.dev
 
     def getActualSize(self, partitions, diskset):
         """Return the actual size allocated for the request in megabytes."""
         part = partedUtils.get_partition_by_name(diskset.disks, self.device)
         if not part:
+            # XXX kickstart might still call this before allocating the partitions
             raise RuntimeError, "Checking the size of a partition which hasn't been allocated yet"
         return partedUtils.getPartSizeMB(part)
 
@@ -410,9 +534,10 @@ class PartitionSpec(RequestSpec):
         if ret is not None:
             return ret
 
-        if (self.size and self.maxSizeMB and (self.size > self.maxSizeMB)):
+        if (self.size and self.maxSizeMB
+            and (self.size > self.maxSizeMB)):
             return (_("The size of the requested partition (size = %s MB) "
-                      "exceeds the maximum size of %s MB.")
+                     "exceeds the maximum size of %s MB.")
                     % (self.size, self.maxSizeMB))
 
         if self.size and self.size < 0:
@@ -430,6 +555,7 @@ class PartitionSpec(RequestSpec):
 class NewPartitionSpec(PartitionSpec):
     """Object to define a NEW requested partition."""
 
+    # XXX eep, still a few too many options but a lot better
     def __init__(self, fstype, size = None, mountpoint = None,
                  grow = 0, maxSizeMB = None,
                  start = None, end = None,
@@ -478,3 +604,444 @@ class PreexistingPartitionSpec(PartitionSpec):
                                format = format, migrate = migrate,
                                mountpoint = mountpoint, preexist = 1)
         self.type = REQUEST_PREEXIST
+        self.resizable = True
+
+        self.maxResizeSize = None
+        """Maximum size of this partition request"""
+
+    def getMaximumResizeMB(self, partitions):
+        if self.maxResizeSize is not None:
+            return self.maxResizeSize
+        log.warning("%s doesn't have a max size set" %(self.device,))
+        return MAX_PART_SIZE
+
+    def getMinimumResizeMB(self, partitions):
+        return self.fstype.getMinimumSize(self.device)
+
+class RaidRequestSpec(RequestSpec):
+    """Request to represent RAID devices."""
+
+    def __init__(self, fstype, format = None, mountpoint = None,
+                 raidlevel = None, raidmembers = None,
+                 raidspares = None, raidminor = None, fslabel = None,
+                 preexist = 0, chunksize = None,
+                 fsprofile = None):
+        """Create a new RaidRequestSpec object.
+
+        fstype is the fsset filesystem type.
+        format is whether or not the partition should be formatted.
+        mountpoint is the mountpoint.
+        raidlevel is the raidlevel (as 'RAID0', 'RAID1', 'RAID5').
+        chunksize is the chunksize which should be used.
+        raidmembers is list of ids corresponding to the members of the RAID.
+        raidspares is the number of spares to setup.
+        raidminor is the minor of the device which should be used.
+        fslabel is the label of the filesystem.
+        fsprofile is the usage profile for the filesystem.
+        """
+
+        # if it's preexisting, the original fstype should be set
+        if preexist == 1:
+            origfs = fstype
+        else:
+            origfs = None
+
+        RequestSpec.__init__(self, fstype = fstype, format = format,
+                             mountpoint = mountpoint, preexist = preexist,
+                             origfstype = origfs,
+                             fslabel=fslabel, fsprofile=fsprofile)
+        self.type = REQUEST_RAID
+
+
+        self.raidlevel = raidlevel
+        self.raidmembers = raidmembers
+        self.raidspares = raidspares
+        self.raidminor = raidminor
+        self.chunksize = chunksize
+
+    def __str__(self):
+        if self.fstype:
+            fsname = self.fstype.getName()
+        else:
+            fsname = "None"
+        raidmem = []
+        if self.raidmembers:
+            for i in self.raidmembers:
+                raidmem.append(i)
+
+        if self.encryption is None:
+            crypto = "None"
+        else:
+            crypto = self.encryption.getScheme()
+
+        str = ("RAID Request -- mountpoint: %(mount)s  uniqueID: %(id)s\n"
+               "  type: %(fstype)s  format: %(format)s\n"
+               "  raidlevel: %(level)s  raidspares: %(spares)s\n"
+               "  raidmembers: %(members)s  fsprofile: %(fsprofile)s\n"
+               "  encryption: %(encryption)s" %
+               {"mount": self.mountpoint, "id": self.uniqueID,
+                "fstype": fsname, "format": self.format,
+                "level": self.raidlevel, "spares": self.raidspares,
+                "members": self.raidmembers, "fsprofile": self.fsprofile,
+                "encryption": crypto
+                })
+        return str
+
+    def getDevice(self, partitions):
+        """Return a device which can be solidified."""
+        # Alway return a new device for minor changing
+        raidmems = []
+        for member in self.raidmembers:
+            request = partitions.getRequestByID(member)
+            raidmems.append(request.getDevice(partitions))
+        self.dev = fsset.RAIDDevice(int(self.raidlevel[4:]),
+                                    raidmems, minor = self.raidminor,
+                                    spares = self.raidspares,
+                                    existing = self.preexist,
+                                    chunksize = self.chunksize,
+                                    encryption = self.encryption)
+        return self.dev
+
+    def isEncrypted(self, partitions, parentOnly = False):
+        if RequestSpec.isEncrypted(self, partitions) is True:
+            return True
+        if parentOnly:
+            return False
+        for member in self.raidmembers:
+            if partitions.getRequestByID(member).isEncrypted(partitions):
+                return True
+        return False
+
+    def getActualSize(self, partitions, diskset):
+        """Return the actual size allocated for the request in megabytes."""
+
+        # this seems like a check which should never fail...
+        if not self.raidmembers or not self.raidlevel:
+            return 0
+        nummembers = len(self.raidmembers) - self.raidspares
+        smallest = None
+        sum = 0
+        for member in self.raidmembers:
+            req = partitions.getRequestByID(member)
+            partsize = req.getActualSize(partitions, diskset)
+
+            if raid.isRaid0(self.raidlevel):
+                sum = sum + partsize
+            else:
+                if not smallest:
+                    smallest = partsize
+                elif partsize < smallest:
+                    smallest = partsize
+
+        if raid.isRaid0(self.raidlevel):
+            return sum
+        elif raid.isRaid1(self.raidlevel):
+            return smallest
+        elif raid.isRaid5(self.raidlevel):
+            return (nummembers-1) * smallest
+        elif raid.isRaid6(self.raidlevel):
+            return (nummembers-2) * smallest
+        elif raid.isRaid10(self.raidlevel):
+            return (nummembers/2) * smallest
+        else:
+            raise ValueError, "Invalid raidlevel in RaidRequest.getActualSize"
+
+
+    # do RAID specific sanity checks; this is an internal function
+    def sanityCheckRaid(self, partitions):
+        if not self.raidmembers or not self.raidlevel:
+            return _("No members in RAID request, or not RAID "
+                     "level specified.")
+
+        minmembers = raid.get_raid_min_members(self.raidlevel)
+        if len(self.raidmembers) < minmembers:
+            return _("A RAID device of type %s "
+                     "requires at least %s members.") % (self.raidlevel,
+                                                         minmembers)
+
+        if len(self.raidmembers) > 27:
+            return "RAID devices are limited to 27 members."
+
+        if self.raidspares:
+            if (len(self.raidmembers) - self.raidspares) < minmembers:
+                return _("This RAID device can have a maximum of %s spares. "
+                         "To have more spares you will need to add members to "
+                         "the RAID device.") % (len(self.raidmembers)
+                                                - minmembers )
+        return None
+
+    def sanityCheckRequest(self, partitions):
+        """Run the basic sanity checks on the request."""
+        rc = self.sanityCheckRaid(partitions)
+        if rc:
+            return rc
+
+        return RequestSpec.sanityCheckRequest(self, partitions)
+
+class VolumeGroupRequestSpec(RequestSpec):
+    """Request to represent volume group devices."""
+
+    def __init__(self, fstype =None, format = None,
+                 vgname = None, physvols = None,
+                 pesize = 32768, preexist = 0,
+                 preexist_size = 0):
+        """Create a new VolumeGroupRequestSpec object.
+
+        fstype is the fsset filesystem type.
+        format is whether or not the volume group should be created.
+        vgname is the name of the volume group.
+        physvols is a list of the ids for the physical volumes in the vg.
+        pesize is the size of a physical extent in kilobytes.
+        preexist is whether the volume group is preexisting.
+        preexist_size is the size of a preexisting VG read from /proc
+            (note that this is unclamped)
+        """
+
+        if not fstype:
+            fstype = fsset.fileSystemTypeGet("volume group (LVM)")
+        RequestSpec.__init__(self, fstype = fstype, format = format)
+        self.type = REQUEST_VG
+
+        self.volumeGroupName = vgname
+        self.physicalVolumes = physvols
+        self.pesize = pesize
+        self.preexist = preexist
+        self.free = 0
+
+        # FIXME: this is a hack so that we can set the vg name automagically
+        # with autopartitioning to not conflict with existing vgs
+        self.autoname = 0
+
+        if preexist and preexist_size:
+            self.preexist_size = preexist_size
+        else:
+            self.preexist_size = None
+
+    def __str__(self):
+        physvols = []
+        if self.physicalVolumes:
+            for i in self.physicalVolumes:
+                physvols.append(i)
+
+        str = ("VG Request -- name: %(vgname)s  uniqueID: %(id)s\n"
+               "  format: %(format)s pesize: %(pesize)s  \n"
+               "  physvols: %(physvol)s" %
+               {"vgname": self.volumeGroupName, "id": self.uniqueID,
+                "format": self.format, "physvol": physvols,
+                "pesize": self.pesize})
+        return str
+
+    def getDevice(self, partitions):
+        """Return a device which can be solidified."""
+        if self.dev:
+            # FIXME: this warning can probably be removed post-beta
+            log.warning("getting self.dev more than once for %s" %(self,))
+            return self.dev
+
+        pvs = []
+        for pv in self.physicalVolumes:
+            r = partitions.getRequestByID(pv)
+            # a size of zero implies we did autopartitioning of
+            # pvs everywhere we could
+            if (r.size > 0) or (r.device is not None):
+                pvs.append(r.getDevice(partitions))
+        self.dev = fsset.VolumeGroupDevice(self.volumeGroupName, pvs,
+                                           self.pesize,
+                                           existing = self.preexist)
+        return self.dev
+
+    def isEncrypted(self, partitions, parentOnly = False):
+        if RequestSpec.isEncrypted(self, partitions) is True:
+            return True
+        if parentOnly:
+            return False
+        for pvid in self.physicalVolumes:
+            pv = partitions.getRequestByID(pvid)
+            if pv.isEncrypted(partitions):
+                return True
+        return False
+
+    def getActualSize(self, partitions, diskset):
+        """Return the actual size allocated for the request in megabytes."""
+
+        # if we have a preexisting size, use it
+        if self.preexist and self.preexist_size:
+            totalspace = lvm.clampPVSize(self.preexist_size, self.pesize)
+        else:
+            totalspace = 0
+            for pvid in self.physicalVolumes:
+                pvreq = partitions.getRequestByID(pvid)
+                size = pvreq.getActualSize(partitions, diskset)
+                #log.info("size for pv %s is %s" % (pvid, size))
+                size = lvm.clampPVSize(size, self.pesize) - (self.pesize/1024)
+                #log.info("  clamped size is %s" % (size,))
+                totalspace = totalspace + size
+
+        return totalspace
+
+class PartialVolumeGroupSpec:
+    """Request to represent partial volume group devices."""
+    # note, these are just used as placeholders so we don't collide on names
+
+    def __init__(self, vgname = None):
+        """Create a new PartialVolumeGroupSpec object.
+
+        vgname is the name of the volume group.
+        """
+
+        self.volumeGroupName = vgname
+
+    def __str__(self):
+        str = ("Partial VG Request -- name: %(vgname)s" %
+               {"vgname": self.volumeGroupName})
+        return str
+
+class LogicalVolumeRequestSpec(RequestSpec):
+    """Request to represent logical volume devices."""
+
+    def __init__(self, fstype, format = None, mountpoint = None,
+                 size = None, volgroup = None, lvname = None,
+                 preexist = 0, percent = None, grow=0, maxSizeMB=0,
+                 fslabel = None, fsprofile = None):
+        """Create a new VolumeGroupRequestSpec object.
+
+        fstype is the fsset filesystem type.
+        format is whether or not the volume group should be created.
+        mountpoint is the mountpoint for the request.
+        size is the size of the request in MB.
+        volgroup is the request ID of the volume group.
+        lvname is the name of the logical volume.
+        preexist is whether the logical volume previously existed or not.
+        percent is the percentage of the volume group's space this should use.
+        grow is whether or not to use free space remaining.
+        maxSizeMB is max size to grow to.
+        fslabel is the label of the filesystem on the logical volume.
+        fsprofile is the usage profile for the filesystem.
+        """
+
+        # if it's preexisting, the original fstype should be set
+        if preexist == 1:
+            origfs = fstype
+        else:
+            origfs = None
+
+        RequestSpec.__init__(self, fstype = fstype, format = format,
+                             mountpoint = mountpoint, size = size,
+                             preexist = preexist, origfstype = origfs,
+                             fslabel = fslabel, fsprofile = fsprofile)
+
+        self.type = REQUEST_LV
+
+        self.logicalVolumeName = lvname
+        self.volumeGroup = volgroup
+        self.percent = percent
+        self.grow = grow
+        self.maxSizeMB = maxSizeMB
+        self.startSize = size
+
+        self.minResizeSize = None
+        self.resizable = True
+
+        if not percent and not size and not preexist:
+            raise RuntimeError, "Error with Volume Group:Logical Volume %s:%s - Logical Volume must specify either percentage of vgsize or size" % (volgroup, lvname)
+
+        if percent and grow:
+            raise RuntimeError, "Error with Volume Group:Logical Volume %s:%s - Logical Volume cannot grow if percentage given" % (volgroup, lvname)
+
+    def __str__(self):
+        if self.fstype:
+            fsname = self.fstype.getName()
+        else:
+            fsname = "None"
+
+        if self.size is not None:
+            size = self.size
+        else:
+            size = "%s percent" %(self.percent,)
+
+        if self.encryption is None:
+            crypto = "None"
+        else:
+            crypto = self.encryption.getScheme()
+
+        str = ("LV Request -- mountpoint: %(mount)s  uniqueID: %(id)s\n"
+               "  type: %(fstype)s  format: %(format)s\n"
+               "  size: %(size)s  lvname: %(lvname)s  volgroup: %(vgid)s\n"
+               "  options: '%(fsopts)s'  fsprofile: %(fsprofile)s"
+               "  encryption: '%(crypto)s'" %
+               {"mount": self.mountpoint, "id": self.uniqueID,
+                "fstype": fsname, "format": self.format,
+                "lvname": self.logicalVolumeName, "vgid": self.volumeGroup,
+                "size": size, "crypto": crypto,
+                "fsopts": self.fsopts, "fsprofile": self.fsprofile})
+        return str
+
+    def getDevice(self, partitions):
+        """Return a device which can be solidified."""
+        vg = partitions.getRequestByID(self.volumeGroup)
+        vgname = vg.volumeGroupName
+        self.dev = fsset.LogicalVolumeDevice(vgname, self.size,
+                                             self.logicalVolumeName,
+                                             vg = vg,
+                                             existing = self.preexist,
+                                             encryption = self.encryption)
+        return self.dev
+
+    def isEncrypted(self, partitions, parentOnly = False):
+        if RequestSpec.isEncrypted(self, partitions) is True:
+            return True
+        if parentOnly:
+            return False
+        vg = partitions.getRequestByID(self.volumeGroup)
+        if vg.isEncrypted(partitions):
+            return True
+        return False
+
+    def getActualSize(self, partitions = None, diskset = None, target = False):
+        """Return the actual size allocated for the request in megabytes."""
+        if self.percent:
+            if partitions is None or diskset is None:
+                raise RuntimeError, "trying to get a percentage lv size on resize path"
+            vgreq = partitions.getRequestByID(self.volumeGroup)
+            vgsize = vgreq.getActualSize(partitions, diskset)
+            lvsize = int(self.percent * 0.01 * vgsize)
+            #lvsize = lvm.clampLVSizeRequest(lvsize, vgreq.pesize)
+            return lvsize
+        # FIXME: the target bit here is a bit of a hack...
+        elif self.targetSize is not None and target:
+            return self.targetSize
+        else:
+            return self.size
+
+    def getStartSize(self):
+        """Return the starting size allocated for the request in megabytes."""
+        return self.startSize
+
+    def setSize(self, size):
+        """Set the size (in MB) of request (does not clamp to PE however)
+
+        size - size in MB
+        """
+        if self.percent:
+            self.percent = None
+
+        self.size = size
+
+    def sanityCheckRequest(self, partitions, skipMntPtExistCheck=0, pesize=32768):
+        """Run the basic sanity checks on the request."""
+        if not self.grow and not self.percent and self.size*1024 < pesize:
+            return _("Logical volume size must be larger than the volume "
+                     "group's physical extent size.")
+
+        return RequestSpec.sanityCheckRequest(self, partitions, skipMntPtExistCheck)
+
+    def getMaximumResizeMB(self, partitions):
+        vg = partitions.getRequestByID(self.volumeGroup)
+        print("max is", self.getActualSize(), vg.free, self.getActualSize() + vg.free)
+        return self.getActualSize() + vg.free
+
+    def getMinimumResizeMB(self, partitions):
+        if self.minResizeSize is None:
+            log.warning("don't know the minimum size of %s" %(self.logicalVolumeName,))
+            return 1
+        return self.minResizeSize
index c1f142ce27417feecced603d57cb9e81c0317bd1..cee5a1e7bd0ed71cc1f433641b1d6e254ba68d27 100644 (file)
@@ -1,39 +1,52 @@
 #
 # partedUtils.py: helper functions for use with parted objects
 #
-# Matt Wilson <msw@redhat.com>
-# Jeremy Katz <katzj@redhat.com>
-# Mike Fulbright <msf@redhat.com>
-# Karsten Hopp <karsten@redhat.com>
+# Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007  Red Hat, Inc.
+# All rights reserved.
 #
-# Copyright 2002-2003 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): Matt Wilson <msw@redhat.com>
+#            Jeremy Katz <katzj@redhat.com>
+#            Mike Fulbright <msf@redhat.com>
+#            Karsten Hopp <karsten@redhat.com>
+#
+
 """Helper functions for use when dealing with parted objects."""
 
 import parted
 import math
 import os, sys, string, struct, resource
 
+import exception
 import fsset
-import inutil, isys
-import pyfire
+import iutil, isys
+import raid
+import dmraid
+import block
+import lvm
+import traceback
 from flags import flags
-from partErrors import *
+from errors import *
 from constants import *
 
-import gettext
-_ = lambda x: gettext.ldgettext("pomona", x)
-
 import logging
 log = logging.getLogger("pomona")
 
+import gettext
+_ = lambda x: gettext.ldgettext("pomona", x)
+
 fsTypes = {}
 
 fs_type = parted.file_system_type_get_next ()
@@ -41,7 +54,9 @@ while fs_type:
     fsTypes[fs_type.name] = fs_type
     fs_type = parted.file_system_type_get_next (fs_type)
 
-def get_flags(part):
+
+
+def get_flags (part):
     """Retrieve a list of strings representing the flags on the partition."""
     string=""
     if not part.is_active ():
@@ -55,16 +70,18 @@ def get_flags(part):
                 first = 0
             else:
                 string = string + ", "
-        flag = parted.partition_flag_next(flag)
+        flag = parted.partition_flag_next (flag)
     return string
 
 def start_sector_to_cyl(device, sector):
     """Return the closest cylinder (round down) to sector on device."""
-    return int(math.floor((float(sector) / (device.heads * device.sectors)) + 1))
+    return int(math.floor((float(sector)
+                           / (device.heads * device.sectors)) + 1))
 
 def end_sector_to_cyl(device, sector):
     """Return the closest cylinder (round up) to sector on device."""
-    return int(math.ceil(float((sector + 1)) / (device.heads * device.sectors)))
+    return int(math.ceil(float((sector + 1))
+                         / (device.heads * device.sectors)))
 
 def start_cyl_to_sector(device, cyl):
     "Return the sector corresponding to cylinder as a starting cylinder."
@@ -80,11 +97,31 @@ def getPartSize(partition):
 
 def getPartSizeMB(partition):
     """Return the size of partition in megabytes."""
-    return (partition.geom.length * partition.geom.dev.sector_size / 1024.0 / 1024.0)
+    return (partition.geom.length * partition.geom.dev.sector_size
+            / 1024.0 / 1024.0)
 
 def getDeviceSizeMB(dev):
     """Return the size of dev in megabytes."""
-    return (float(dev.heads * dev.cylinders * dev.sectors) / (1024 * 1024) * dev.sector_size)
+    return (float(dev.heads * dev.cylinders * dev.sectors) / (1024 * 1024)
+            * dev.sector_size)
+
+def getMaxAvailPartSizeMB(part):
+    """Return the maximum size this partition can grow to by looking
+    at contiguous freespace partitions."""
+
+    disk = part.disk
+    maxlen = part.geom.length
+
+    # let's look at the next partition(s) and if they're freespace,
+    # they can add to our maximum size.
+    np = disk.next_partition(part)
+    while np:
+        if np.type & parted.PARTITION_FREESPACE:
+            maxlen += np.geom.length
+        else:
+            break
+        np = disk.next_partition(np)
+    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.
@@ -110,16 +147,18 @@ def get_partition_by_name(disks, partname):
 def get_partition_name(partition):
     """Return the device name for the PedPartition partition."""
     if (partition.geom.dev.type == parted.DEVICE_DAC960
-                    or partition.geom.dev.type == parted.DEVICE_CPQARRAY):
-        return "%sp%d" % (partition.geom.dev.path[5:], partition.num)
+        or partition.geom.dev.type == parted.DEVICE_CPQARRAY):
+        return "%sp%d" % (partition.geom.dev.path[5:],
+                          partition.num)
     if (parted.__dict__.has_key("DEVICE_SX8") and
-                    partition.geom.dev.type == parted.DEVICE_SX8):
-        return "%sp%d" % (partition.geom.dev.path[5:], partition.num)
+        partition.geom.dev.type == parted.DEVICE_SX8):
+        return "%sp%d" % (partition.geom.dev.path[5:],
+                          partition.num)
 
     drive = partition.geom.dev.path[5:]
     if (drive.startswith("cciss") or drive.startswith("ida") or
-                    drive.startswith("rd") or drive.startswith("sx8") or
-                    drive.startswith("mapper")):
+            drive.startswith("rd") or drive.startswith("sx8") or
+            drive.startswith("mapper") or drive.startswith("mmcblk")):
         sep = "p"
     else:
         sep = ""
@@ -139,10 +178,16 @@ def get_partition_file_system_type(part):
         ptype = fsset.fileSystemTypeGet("PPC PReP Boot")
     elif part.fs_type == None:
         return None
+    elif (part.get_flag(parted.PARTITION_BOOT) == 1 and
+          getPartSizeMB(part) <= 1 and part.fs_type.name == "hfs"):
+        ptype = fsset.fileSystemTypeGet("Apple Bootstrap")
     elif part.fs_type.name == "linux-swap":
         ptype = fsset.fileSystemTypeGet("swap")
-    elif (part.fs_type.name == "FAT" or part.fs_type.name == "fat16"
-                            or part.fs_type.name == "fat32"):
+    elif isEfiSystemPartition(part):
+        ptype = fsset.fileSystemTypeGet("efi")
+    elif isEfiSystemPartition(part):
+        ptype = fsset.fileSystemTypeGet("efi")
+    elif part.fs_type.name in ("fat16", "fat32"):
         ptype = fsset.fileSystemTypeGet("vfat")
     else:
         try:
@@ -161,16 +206,19 @@ def set_partition_file_system_type(part, fstype):
         for flag in fstype.getPartedPartitionFlags():
             if not part.is_flag_available(flag):
                 raise PartitioningError, ("requested FileSystemType needs "
-                                                                                                                        "a flag that is not available.")
+                                          "a flag that is not available.")
             part.set_flag(flag, 1)
+        if isEfiSystemPartition(part):
+            part.set_system(parted.file_system_type_get("fat32"))
+        else:
             part.set_system(fstype.getPartedFileSystemType())
     except:
-        print "Failed to set partition type to ",fstype.getName()
+        print("Failed to set partition type to ",fstype.getName())
         pass
 
 def get_partition_drive(partition):
     """Return the device name for disk that PedPartition partition is on."""
-    return "%s" % (partition.geom.dev.path[5:])
+    return "%s" %(partition.geom.dev.path[5:])
 
 def get_max_logical_partitions(disk):
     if not disk.type.check_feature(parted.DISK_TYPE_EXTENDED):
@@ -196,7 +244,7 @@ def filter_partitions(disk, func):
     while part:
         if func(part):
             rc.append(part)
-        part = disk.next_partition(part)
+        part = disk.next_partition (part)
 
     return rc
 
@@ -207,7 +255,8 @@ def get_all_partitions(disk):
 
 def get_logical_partitions(disk):
     """Return a list of logical PedPartition objects on disk."""
-    func = lambda part: (part.is_active() and part.type & parted.PARTITION_LOGICAL)
+    func = lambda part: (part.is_active()
+                         and part.type & parted.PARTITION_LOGICAL)
     return filter_partitions(disk, func)
 
 def get_primary_partitions(disk):
@@ -215,15 +264,64 @@ def get_primary_partitions(disk):
     func = lambda part: part.type == parted.PARTITION_PRIMARY
     return filter_partitions(disk, func)
 
+def get_raid_partitions(disk):
+    """Return a list of RAID-type PedPartition objects on disk."""
+    func = lambda part: (part.is_active()
+                         and part.get_flag(parted.PARTITION_RAID) == 1)
+    return filter_partitions(disk, func)
+
+def get_lvm_partitions(disk):
+    """Return a list of physical volume-type PedPartition objects on disk."""
+    func = lambda part: (part.is_active()
+                         and part.get_flag(parted.PARTITION_LVM) == 1)
+    return filter_partitions(disk, func)
+
+
 def getDefaultDiskType():
     """Get the default partition table type for this architecture."""
     return parted.disk_type_get("msdos")
 
-archLabels = {'i386': ['msdos', 'gpt']}
+def hasGptLabel(diskset, device):
+    disk = diskset.disks[device]
+    return disk.type.name == "gpt"
+
+def isEfiSystemPartition(part):
+    if not part.is_active():
+        return False
+    return (part.disk.type.name == "gpt" and
+            part.get_name() == "EFI System Partition" and
+            part.get_flag(parted.PARTITION_BOOT) == 1 and
+            part.fs_type.name in ("fat16", "fat32") and
+            isys.readFSLabel(get_partition_name(part)) != "pomona")
+
+archLabels = {'i386': ['msdos', 'gpt'],
+              's390': ['dasd', 'msdos'],
+              'alpha': ['bsd', 'msdos'],
+              'sparc': ['sun'],
+              'ia64': ['msdos', 'gpt'],
+              'ppc': ['msdos', 'mac', 'amiga', 'gpt'],
+              'x86_64': ['msdos', 'gpt']}
+
+def labelDisk(deviceFile, forceLabelType=None):
+    dev = parted.PedDevice.get(deviceFile)
+    label = getDefaultDiskType()
+
+    if not forceLabelType is None:
+        label = forceLabelType
+    else:
+        if label.name == 'msdos' and \
+                dev.length > (2L**41) / dev.sector_size and \
+                'gpt' in archLabels[iutil.getArch()]:
+            label = parted.disk_type_get('gpt')
+
+    disk = dev.disk_new_fresh(label)
+    disk.commit()
+    return disk
 
 def checkDiskLabel(disk, intf):
     """Check that the disk label on disk is valid for this machine type."""
-    if disk.type.name == "msdos":
+    arch = iutil.getArch()
+    if not arch in archLabels.keys() and disk.type.name == "msdos":
         return 0
 
     if intf:
@@ -235,9 +333,11 @@ def checkDiskLabel(disk, intf):
                                   "ALL DATA on this drive.\n\n"
                                   "Would you like to re-initialize this "
                                   "drive?")
-                                %(disk.dev.path[5:], disk.type.name, name),
-                                  type="custom", custom_buttons = [ _("_Ignore drive"),
-                                  _("_Re-initialize drive") ], custom_icon="question")
+                                %(disk.dev.path[5:], disk.type.name,
+                                  name), type="custom",
+                                custom_buttons = [ _("_Ignore drive"),
+                                                   _("_Re-initialize drive") ],
+                                custom_icon="question")
 
         if rc == 0:
             return 1
@@ -246,11 +346,31 @@ def checkDiskLabel(disk, intf):
     else:
         return 1
 
+def hasProtectedPartitions(drive, pomona):
+    rc = False
+    if pomona is None:
+        return rc
+
+    try:
+        for protected in pomona.id.partitions.protectedPartitions():
+            if protected.startswith(drive):
+                part = protected[len(drive):]
+                if part[0] == "p":
+                    part = part[1:]
+                if part.isdigit():
+                    rc = True
+                    break
+    except:
+        pass
+
+    return rc
+
 # attempt to associate a parted filesystem type on a partition that
 # didn't probe as one type or another.
 def validateFsType(part):
     # we only care about primary and logical partitions
-    if not part.type in (parted.PARTITION_PRIMARY,  parted.PARTITION_LOGICAL):
+    if not part.type in (parted.PARTITION_PRIMARY,
+                         parted.PARTITION_LOGICAL):
         return
     # if the partition already has a type, no need to search
     if part.fs_type:
@@ -275,7 +395,7 @@ def validateFsType(part):
             # XXX verify that this will not modify system type
             # in the case where a user does not modify partitions
             part.set_system(fstype)
-    return
+            return
 
 def isLinuxNativeByNumtype(numtype):
     """Check if the type is a 'Linux native' filesystem."""
@@ -290,70 +410,64 @@ def isLinuxNativeByNumtype(numtype):
 def sniffFilesystemType(device):
     """Sniff to determine the type of fs on device.
 
-    device - name of device to sniff.  we try to create it if it doesn't exist.
+    device - name of device to sniff.
     """
+    return isys.readFSType(device)
 
-    if os.access(device, os.O_RDONLY):
-        dev = device
-    else:
-        dev = "/tmp/" + device
-
-    pagesize = resource.getpagesize()
-    if pagesize > 2048:
-        num = pagesize
-    else:
-        num = 2048
-
-    try:
-        fd = os.open(dev, os.O_RDONLY)
-        buf = os.read(fd, num)
-    except:
-        return None
-    finally:
-        try:
-            os.close(fd)
-        except:
-            pass
-
-    if len(buf) < pagesize:
+def getReleaseString(mountpoint):
+    if os.access(mountpoint + "/etc/system-release", os.R_OK):
+        f = open(mountpoint + "/etc/system-release", "r")
         try:
-            log.error("Tried to read pagesize for %s in sniffFilesystemType and only read %s", dev, len(buf))
-        except:
-            pass
-        return None
-
-    # ext2 check
-    if struct.unpack("<H", buf[1080:1082]) == (0xef53,):
-        if isys.ext2HasJournal(dev):
-            return "ext3"
-        else:
-            return "ext2"
-
-    # xfs signature
-    if buf.startswith("XFSB"):
-        return "xfs"
-
-    # 2.6 doesn't support version 0, so we don't like SWAP-SPACE
-    if (buf[pagesize - 10:] == "SWAPSPACE2"):
-        return "swap"
-
-    if fsset.isValidReiserFS(dev):
-        return "reiserfs"
+            lines = f.readlines()
+        except IOError:
+            try:
+                f.close()
+            except:
+                pass
+            return ""
+        f.close()
+        # return the first line with the newline at the end stripped
+        if len(lines) == 0:
+            return ""
+        relstr = string.strip(lines[0][:-1])
+
+        # get the release name and version
+        # assumes that form is something
+        # like "Red Hat Linux release 6.2 (Zoot)"
+        if relstr.find("release") != -1:
+            try:
+                idx = relstr.find("release")
+                prod = relstr[:idx - 1]
 
-    # FIXME:  we don't look for vfat
-    ### XXX Check for reiser4
+                ver = ""
+                for a in relstr[idx + 8:]:
+                    if a in string.digits + ".":
+                        ver = ver + a
+                    else:
+                        break
 
-    return None
+                    relstr = prod + " " + ver
+            except:
+                pass # don't worry, just use the relstr as we have it
+        return relstr
+    return ""
 
 class DiskSet:
     """The disks in the system."""
 
     skippedDisks = []
+    mdList = []
+    exclusiveDisks = []
+
+    dmList = None
+    mpList = None
 
-    def __init__ (self, pomona = None):
+    def __init__ (self, pomona):
         self.disks = {}
+        self.initializedDisks = {}
         self.onlyPrimary = None
         self.pomona = pomona
+        self.devicesOpen = False
 
     def onlyPrimaryParts(self):
         for disk in self.disks.values():
@@ -362,70 +476,296 @@ class DiskSet:
 
         return 1
 
-    def getLabels(self):
-        """Return a list of all of the labels used on partitions."""
-        labels = {}
+    def startMPath(self):
+        """Start all of the dm multipath devices associated with the DiskSet."""
 
-        drives = self.disks.keys()
-        drives.sort()
+        if not DiskSet.mpList is None and DiskSet.mpList.__len__() > 0:
+            return
+
+        log.debug("starting mpaths")
+        log.debug("self.driveList(): %s" % (self.driveList(),))
+        log.debug("DiskSet.skippedDisks: %s" % (DiskSet.skippedDisks,))
+        driveList = filter(lambda x: x not in DiskSet.skippedDisks,
+                self.driveList())
+        log.debug("DiskSet.skippedDisks: %s" % (DiskSet.skippedDisks,))
+
+        mpList = dmraid.startAllMPath(driveList)
+        DiskSet.mpList = mpList
+        log.debug("done starting mpaths.  Drivelist: %s" % \
+            (self.driveList(),))
+
+    def renameMPath(self, mp, name):
+        dmraid.renameMPath(mp, name)
+
+    def stopMPath(self):
+        """Stop all of the mpath devices associated with the DiskSet."""
+
+        if DiskSet.mpList:
+            dmraid.stopAllMPath(DiskSet.mpList)
+            DiskSet.mpList = None
+
+    def startDmRaid(self):
+        """Start all of the dmraid devices associated with the DiskSet."""
+
+        if not DiskSet.dmList is None:
+            return
+
+        log.debug("starting dmraids")
+        log.debug("self.driveList(): %s" % (self.driveList(),))
+        log.debug("DiskSet.skippedDisks: %s" % (DiskSet.skippedDisks,))
+        driveList = filter(lambda x: x not in DiskSet.skippedDisks,
+                self.driveList())
+        log.debug("DiskSet.skippedDisks: %s" % (DiskSet.skippedDisks,))
+
+        dmList = dmraid.startAllRaid(driveList)
+        DiskSet.dmList = dmList
+        log.debug("done starting dmraids.  Drivelist: %s" % \
+            (self.driveList(),))
+
+    def renameDmRaid(self, rs, name):
+        dmraid.renameRaidSet(rs, name)
+
+    def stopDmRaid(self):
+        """Stop all of the dmraid devices associated with the DiskSet."""
+        if DiskSet.dmList:
+            dmraid.stopAllRaid(DiskSet.dmList)
+            DiskSet.dmList = None
+
+    def startMdRaid(self):
+        """Start all of the md raid devices associated with the DiskSet."""
+
+        testList = []
+        testList.extend(DiskSet.skippedDisks)
+
+        for mp in DiskSet.mpList or []:
+            for m in mp.members:
+                disk = m.split('/')[-1]
+                testList.append(disk)
+
+        for rs in DiskSet.dmList or []:
+            for m in rs.members:
+                if isinstance(m, block.RaidDev):
+                    disk = m.rd.device.path.split('/')[-1]
+                    testList.append(disk)
+
+        driveList = filter(lambda x: x not in testList, self.driveList())
+        DiskSet.mdList.extend(raid.startAllRaid(driveList))
+
+    def stopMdRaid(self):
+        """Stop all of the md raid devices associated with the DiskSet."""
+
+        raid.stopAllRaid(DiskSet.mdList)
+
+        while DiskSet.mdList:
+            DiskSet.mdList.pop()
+
+    def getInfo(self, readFn=lambda d: isys.readFSLabel(d)):
+        """Return a dict keyed on device name, storing some sort of data
+           about each device.  This is typially going to be labels or UUIDs,
+           as required by readFstab.
+        """
+        ret = {}
+
+        encryptedDevices = self.pomona.id.partitions.encryptedDevices
+
+        for drive in self.driveList():
+            # Don't read labels from drives we cleared using clearpart, as
+            # we don't actually remove the existing filesystems so those
+            # labels will still be present (#209291).
+            if drive in DiskSet.skippedDisks:
+                continue
+
+            # ignoredisk takes precedence over clearpart (#186438).
+            if DiskSet.exclusiveDisks != [] and \
+                    drive not in DiskSet.exclusiveDisks:
+                continue
 
-        for drive in drives:
             disk = self.disks[drive]
             func = lambda part: (part.is_active() and
-                          not (part.get_flag(parted.PARTITION_RAID)
-                          or part.get_flag(parted.PARTITION_LVM)))
+                                 not (part.get_flag(parted.PARTITION_RAID)
+                                      or part.get_flag(parted.PARTITION_LVM)))
             parts = filter_partitions(disk, func)
             for part in parts:
                 node = get_partition_name(part)
-                label = isys.readFSLabel(node)
-                if label:
-                    labels[node] = label
+                crypto = encryptedDevices.get(node)
+                if crypto and not crypto.openDevice():
+                    node = crypto.getDevice()
+
+                val = readFn(node)
+                if val:
+                    ret[node] = val
+
+                if crypto:
+                    crypto.closeDevice()
+
+        for dev, devices, level, numActive in DiskSet.mdList:
+            crypto = encryptedDevices.get(dev)
+            if crypto and not crypto.openDevice():
+                dev = crypto.getDevice()
+
+            val = readFn(dev)
+            if val:
+                ret[dev] = val
+
+            if crypto:
+                crypto.closeDevice()
+
+        active = lvm.vgcheckactive()
+        if not active:
+            lvm.vgscan()
+            lvm.vgactivate()
+
+        for (vg, lv, size, lvorigin) in lvm.lvlist():
+            if lvorigin:
+                continue
+            node = "%s/%s" % (vg, lv)
+            crypto = encryptedDevices.get(node)
+            if crypto and not crypto.openDevice():
+                node = crypto.getDevice()
 
-        return labels
+            val = readFn("/dev/" + node)
+            if val:
+                ret[node] = val
+
+            if crypto:
+                crypto.closeDevice()
+
+        if not active:
+            lvm.vgdeactivate()
+
+        return ret
 
     def findExistingRootPartitions(self, upgradeany = 0):
         """Return a list of all of the partitions which look like a root fs."""
         rootparts = []
 
+        self.startMPath()
+        self.startDmRaid()
+        self.startMdRaid()
+
+        for dev, crypto in self.pomona.id.partitions.encryptedDevices.items():
+            # FIXME: order these so LVM and RAID always work on the first try
+            if crypto.openDevice():
+                log.error("failed to open encrypted device %s" % (dev,))
+
+        if flags.cmdline.has_key("upgradeany"):
+            upgradeany = 1
+
+        for dev, devices, level, numActive in self.mdList:
+            (errno, msg) = (None, None)
+            found = 0
+            theDev = dev
+            crypto = self.pomona.id.partitions.encryptedDevices.get(dev)
+            if crypto and not crypto.openDevice():
+                theDev = "/dev/%s" % (crypto.getDevice(),)
+            elif crypto:
+                log.error("failed to open encrypted device %s" % dev)
+
+            fs = isys.readFSType(theDev)
+            if fs is not None:
+                try:
+                    isys.mount(theDev, self.pomona.rootPath, fs, readOnly = 1)
+                    found = 1
+                except SystemError:
+                    pass
+
+            if found:
+                isys.umount(self.pomona.rootPath)
+
+        # now, look for candidate lvm roots
+        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
+            if crypto.openDevice():
+                log.error("failed to open encrypted device %s" % (dev,))
+
+        for (vg, lv, size, lvorigin) in lvm.lvlist():
+            if lvorigin:
+                continue
+            dev = "/dev/%s/%s" %(vg, lv)
+            found = 0
+            theDev = dev
+            node = "%s/%s" % (vg, lv)
+            dmnode = "mapper/%s-%s" % (vg, lv)
+            crypto = self.pomona.id.partitions.encryptedDevices.get(dmnode)
+            if crypto and not crypto.openDevice():
+                theDev = "/dev/%s" % (crypto.getDevice(),)
+            elif crypto:
+                log.error("failed to open encrypted device %s" % dev)
+
+            fs = isys.readFSType(theDev)
+            if fs is not None:
+                try:
+                    isys.mount(theDev, self.pomona.rootPath, fs, readOnly = 1)
+                    found = 1
+                except SystemError:
+                    pass
+
+            if found:
+                isys.umount(self.pomona.rootPath)
+
+        lvm.vgdeactivate()
+
+        # don't stop raid until after we've looked for lvm on top of it
+        self.stopMdRaid()
+
         drives = self.disks.keys()
         drives.sort()
 
+        protected = self.pomona.id.partitions.protectedPartitions()
+
         for drive in drives:
             disk = self.disks[drive]
-            part = disk.next_partition()
+            part = disk.next_partition ()
             while part:
-                if (part.is_active() and (part.get_flag(parted.PARTITION_RAID)
-                        or part.get_flag(parted.PARTITION_LVM))):
-                    pass
-                elif (part.fs_type and part.fs_type.name in fsset.getUsableLinuxFs()):
-                    node = get_partition_name(part)
-
-                try:
-                    isys.mount(node, self.pomona.rootPath, part.fs_type.name)
-                except SystemError, (errno, msg):
+                node = get_partition_name(part)
+                crypto = self.pomona.id.partitions.encryptedDevices.get(node)
+                if (part.is_active()
+                    and (part.get_flag(parted.PARTITION_RAID)
+                         or part.get_flag(parted.PARTITION_LVM))):
                     part = disk.next_partition(part)
                     continue
+                elif part.fs_type or crypto:
+                    theDev = node
+                    if part.fs_type:
+                        fstype = part.fs_type.name
 
-                if os.access(self.pomona.rootPath + '/etc/fstab', os.R_OK):
-                    rootparts.append ((node, part.fs_type.name))
+                    if crypto and not crypto.openDevice():
+                        theDev = crypto.getDevice()
+                        fstype = isys.readFSType("/dev/%s" % theDev)
+                    elif crypto:
+                        log.error("failed to open encrypted device %s" % node)
 
-                isys.umount(self.pomona.rootPath)
+                    if not fstype or fstype not in fsset.getUsableLinuxFs():
+                        part = disk.next_partition(part)
+                        continue
 
-                part = disk.next_partition(part)
+                    try:
+                        isys.mount("/dev/%s" % (theDev,),
+                                   self.pomona.rootPath, fstype)
+                        checkRoot = self.pomona.rootPath
+                    except SystemError:
+                        part = disk.next_partition(part)
+                        continue
+
+                    isys.umount(self.pomona.rootPath)
 
+                part = disk.next_partition(part)
         return rootparts
 
-    def driveList(self):
+    def driveList (self):
         """Return the list of drives on the system."""
         drives = isys.hardDriveDict().keys()
         drives.sort (isys.compareDrives)
         return drives
 
-    def drivesByName(self):
+    def drivesByName (self):
         """Return a dictionary of the drives on the system."""
         return isys.hardDriveDict()
 
-    def savePartitions(self):
+    def savePartitions (self):
         """Write the partition tables out to the disks."""
         for disk in self.disks.values():
             if disk.dev.path[5:].startswith("sd") and disk.get_last_partition_num() > 15:
@@ -443,163 +783,219 @@ 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()
 
-    def refreshDevices(self):
+    def _addDisk(self, drive, disk):
+        log.debug("adding drive %s to disk list" % (drive,))
+        self.initializedDisks[drive] = True
+        self.disks[drive] = disk
+
+    def _removeDisk(self, drive, addSkip=True):
+        msg = "removing drive %s from disk lists" % (drive,)
+        if addSkip:
+            msg += "; adding to skip list"
+        log.debug(msg)
+
+        if self.disks.has_key(drive):
+            del self.disks[drive]
+        if addSkip:
+            if self.initializedDisks.has_key(drive):
+                del self.initializedDisks[drive]
+            DiskSet.skippedDisks.append(drive)
+
+    def refreshDevices (self):
         """Reread the state of the disks as they are on disk."""
         self.closeDevices()
         self.disks = {}
         self.openDevices()
 
-    def closeDevices(self):
+    def closeDevices (self):
         """Close all of the disks which are open."""
+        self.stopDmRaid()
+        self.stopMPath()
         for disk in self.disks.keys():
             #self.disks[disk].close()
             del self.disks[disk]
+        self.devicesOpen = False
+
+    def _askForLabelPermission(self, intf, drive, clearDevs, initAll, ks):
+        #Do not try to initialize device's part. table in rescue mode
+        if self.pomona.rescue:
+            self._removeDisk(drive)
+            return False
+
+        rc = 0
+        if (ks and (drive in clearDevs) and initAll):
+            rc = 1
+        elif intf:
+            deviceFile = "/dev/" + drive
+            dev = parted.PedDevice.get(deviceFile)
 
-    def clearDevices(self):
-        def inClearDevs (drive, clearDevs):
-            return (clearDevs is None) or (len(clearDevs) == 0) or (drive in clearDevs)
+            msg = _("The partition table on device %s (%s %-0.f MB) was unreadable.\n\n"
+                    "To create new partitions it must be initialized, "
+                    "causing the loss of ALL DATA on this drive.\n\n"
+                    "This operation will override any previous "
+                    "installation choices about which drives to "
+                    "ignore.\n\n"
+                    "Would you like to initialize this drive, "
+                    "erasing ALL DATA?") % (drive, dev.model, getDeviceSizeMB (dev),)
 
-        clearDevs = []
+            rc = intf.messageWindow(_("Warning"), msg, type="yesno")
 
-        for drive in self.driveList():
-            # ignoredisk takes precedence over clearpart (#186438).
-            if drive in DiskSet.skippedDisks:
-                continue
+        if rc != 0:
+            return True
 
-            deviceFile = "/dev/" + drive
+        self._removeDisk(drive)
+        return False
 
-            if not isys.mediaPresent(drive):
-                DiskSet.skippedDisks.append(drive)
-                continue
+    def _labelDevice(self, drive):
+        log.info("Reinitializing label for drive %s" % (drive,))
+
+        deviceFile = "/dev/" + drive
 
+        try:
             try:
-                dev = parted.PedDevice.get(deviceFile)
+                # FIXME: need the right fix for z/VM formatted dasd
+                disk = labelDisk(deviceFile)
             except parted.error, msg:
-                DiskSet.skippedDisks.append(drive)
-                continue
+                log.error("parted error: %s" % (msg,))
+                raise
+        except:
+            (type, value, tb) = sys.exc_info()
+            lines = exception.formatException(type, value, tb)
+            for line in lines:
+                log.error(line)
+            self._removeDisk(drive)
+            raise LabelError, drive
+
+        self._addDisk(drive, disk)
+        return disk, deviceFile
 
-    def openDevices(self):
+    def openDevices (self):
         """Open the disks on the system and skip unopenable devices."""
 
         if self.disks:
             return
+        self.startMPath()
+        self.startDmRaid()
 
-        if self.pomona is None:
-            intf = None
-            zeroMbr = None
-        else:
-            intf = self.pomona.intf
-            zeroMbr = self.pomona.id.partitions.zeroMbr
+        intf = self.pomona.intf
+        zeroMbr = self.pomona.id.partitions.zeroMbr
 
         for drive in self.driveList():
             # ignoredisk takes precedence over clearpart (#186438).
             if drive in DiskSet.skippedDisks:
                 continue
-            deviceFile = "/dev/" + drive
+
+            if DiskSet.exclusiveDisks != [] and \
+                    drive not in DiskSet.exclusiveDisks:
+                continue
+
             if not isys.mediaPresent(drive):
                 DiskSet.skippedDisks.append(drive)
                 continue
 
+            disk = None
+            dev = None
+
+            if self.initializedDisks.has_key(drive):
+                if not self.disks.has_key(drive):
+                    try:
+                        dev = parted.PedDevice.get("/dev/%s" % (drive,))
+                        disk = parted.PedDisk.new(dev)
+                        self._addDisk(drive, disk)
+                    except parted.error, msg:
+                        self._removeDisk(drive)
+                continue
+
+            ks = False
             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):
+                try:
+                    disk, dev = self._labelDevice(drive)
+                except:
+                    continue
+
             try:
-                dev = parted.PedDevice.get(deviceFile)
+                if not dev:
+                    dev = parted.PedDevice.get("/dev/%s" % (drive,))
+                    disk = None
             except parted.error, msg:
-                DiskSet.skippedDisks.append(drive)
+                log.debug("parted error: %s" % (msg,))
+                self._removeDisk(drive, disk)
                 continue
 
             try:
-                disk = parted.PedDisk.new(dev)
-                self.disks[drive] = disk
+                if not disk:
+                    disk = parted.PedDisk.new(dev)
+                    self._addDisk(drive, disk)
             except parted.error, msg:
                 recreate = 0
                 if zeroMbr:
                     log.error("zeroMBR was set and invalid partition table "
                               "found on %s" % (dev.path[5:]))
                     recreate = 1
-                elif intf is None:
-                    DiskSet.skippedDisks.append(drive)
-                    continue
                 else:
-                    format = drive
-
-                    # if pomona is None here, we are called from labelFactory
-                    if self.pomona is not None:
-                        rc = intf.messageWindow(_("Warning"),
-                                                _("The partition table on device %s was unreadable. "
-                                                  "To create new partitions it must be initialized, "
-                                                  "causing the loss of ALL DATA on this drive.\n\n"
-                                                  "This operation will override any previous "
-                                                  "installation choices about which drives to "
-                                                  "ignore.\n\n"
-                                                  "Would you like to initialize this drive, "
-                                                  "erasing ALL DATA?") % (format,), type = "yesno")
-                        if rc == 0:
-                            DiskSet.skippedDisks.append(drive)
-                            continue
-                        elif rc != 0:
-                            recreate = 1
-                    else:
-                        DiskSet.skippedDisks.append(drive)
+                    if not self._askForLabelPermission(intf, drive, clearDevs,
+                            initAll, ks):
                         continue
 
-                if recreate == 1:
-                    try:
-                        disk = dev.disk_new_fresh(getDefaultDiskType())
-                        disk.commit()
-                    except parted.error, msg:
-                        DiskSet.skippedDisks.append(drive)
-                        continue
+                    recreate = 1
 
+                if recreate == 1 and not flags.test:
                     try:
-                        disk = parted.PedDisk.new(dev)
-                        self.disks[drive] = disk
-                    except parted.error, msg:
-                        DiskSet.skippedDisks.append(drive)
+                        disk, dev = self._labelDevice(drive)
+                    except:
                         continue
 
             filter_partitions(disk, validateFsType)
 
             # check for more than 15 partitions (libata limit)
             if drive.startswith('sd') and disk.get_last_partition_num() > 15:
-                rc = intf.messageWindow(_("Warning"),
-                                        _("The drive /dev/%s has more than 15 "
-                                          "partitions on it.  The SCSI "
-                                          "subsystem in the Linux kernel does "
-                                          "not allow for more than 15 partitons "
-                                          "at this time.  You will not be able "
-                                          "to make changes to the partitioning "
-                                          "of this disk or use any partitions "
-                                          "beyond /dev/%s15 in %s")
-                                        % (drive, drive, name), type="custom",
-                                           custom_buttons = [_("_Reboot"), _("_Continue")],
-                                           custom_icon="warning")
+                str = _("The drive /dev/%s has more than 15 partitions on it.  "
+                        "The SCSI subsystem in the Linux kernel does not "
+                        "allow for more than 15 partitons at this time.  You "
+                        "will not be able to make changes to the partitioning "
+                        "of this disk or use any partitions beyond /dev/%s15 "
+                        "in %s") % (drive, drive, name)
+
+                rc = intf.messageWindow(_("Warning"), str,
+                                    type="custom",
+                                    custom_buttons = [_("_Reboot"),
+                                                      _("_Continue")],
+                                    custom_icon="warning")
                 if rc == 0:
                     sys.exit(0)
 
             # check that their partition table is valid for their architecture
             ret = checkDiskLabel(disk, intf)
             if ret == 1:
-                DiskSet.skippedDisks.append(drive)
-                continue
+                self._removeDisk(drive)
             elif ret == -1:
                 try:
-                    disk = dev.disk_new_fresh(getDefaultDiskType())
-                    disk.commit()
-                except parted.error, msg:
-                    DiskSet.skippedDisks.append(drive)
-                    continue
-                try:
-                    disk = parted.PedDisk.new(dev)
-                    self.disks[drive] = disk
-                except parted.error, msg:
-                    DiskSet.skippedDisks.append(drive)
-                    continue
+                    disk, dev = self._labelDevice(drive)
+                except:
+                    pass
+        self.devicesOpen = True
 
-    def partitionTypes(self):
+    def partitionTypes (self):
         """Return list of (partition, partition type) tuples for all parts."""
         rc = []
         drives = self.disks.keys()
@@ -621,16 +1017,16 @@ class DiskSet:
 
         return rc
 
-    def diskState(self):
+    def diskState (self):
         """Print out current disk state.  DEBUG."""
         rc = ""
         for disk in self.disks.values():
             rc = rc + ("%s: %s length %ld, maximum "
-                       "primary partitions: %d\n"
-                       (disk.dev.path,
-                          disk.dev.model,
-                          disk.dev.length,
-                          disk.max_primary_partition_count))
+                       "primary partitions: %d\n" %
+                       (disk.dev.path,
+                        disk.dev.model,
+                        disk.dev.length,
+                        disk.max_primary_partition_count))
 
             part = disk.next_partition()
             if part:
@@ -648,106 +1044,131 @@ class DiskSet:
                         fs_type_name = part.fs_type.name
                     partFlags = get_flags (part)
                     rc = rc + ("%-9s %-12s %-12s %-10ld %-10ld %-10ld %7s\n"
-                        % (device, part.type_name, fs_type_name,
-                           part.geom.start, part.geom.end, part.geom.length, partFlags))
-                    part = disk.next_partition(part)
+                               % (device, part.type_name, fs_type_name,
+                              part.geom.start, part.geom.end, part.geom.length,
+                              partFlags))
+                part = disk.next_partition(part)
         return rc
 
     def checkNoDisks(self):
         """Check that there are valid disk devices."""
         if len(self.disks.keys()) == 0:
             self.pomona.intf.messageWindow(_("No Drives Found"),
-                                           _("An error has occurred - no valid devices were "
-                                             "found on which to create new file systems. "
-                                             "Please check your hardware for the cause "
-                                             "of this problem."))
+                               _("An error has occurred - no valid devices were "
+                                 "found on which to create new file systems. "
+                                 "Please check your hardware for the cause "
+                                 "of this problem."))
             return True
         return False
 
+
+    def exceptionDisks(self, pomona, probe=True):
+        if probe:
+            isys.flushDriveDict()
+            self.refreshDevices()
+
+        drives = []
+        for d in isys.removableDriveDict().items():
+            func = lambda p: p.is_active() and not p.get_flag(parted.PARTITION_RAID) and not p.get_flag(parted.PARTITION_LVM) and p.fs_type.name in ["ext3", "ext2", "fat16", "fat32"]
+
+            disk = self.disks[d[0]]
+            parts = filter_partitions(disk, func)
+
+            if len(parts) == 0:
+                drives.append(d)
+            else:
+                for part in parts:
+                    name = "%s%s" % (part.disk.dev.path, part.num)
+                    drives.append((os.path.basename(name), d[1]))
+
+        return drives
+
 # XXX is this all of the possibilities?
 dosPartitionTypes = [ 1, 6, 7, 11, 12, 14, 15 ]
 
 # master list of partition types
 allPartitionTypesDict = {
-        0 : "Empty",
-        1: "DOS 12-bit FAT",
-        2: "XENIX root",
-        3: "XENIX usr",
-        4: "DOS 16-bit <32M",
-        5: "Extended",
-        6: "DOS 16-bit >=32M",
-        7: "NTFS/HPFS",
-        8: "AIX",
-        9: "AIX bootable",
-        10: "OS/2 Boot Manager",
-        0xb: "Win95 FAT32",
-        0xc: "Win95 FAT32",
-        0xe: "Win95 FAT16",
-        0xf: "Win95 Ext'd",
-        0x10: "OPUS",
-        0x11: "Hidden FAT12",
-        0x12: "Compaq Setup",
-        0x14: "Hidden FAT16 <32M",
-        0x16: "Hidden FAT16",
-        0x17: "Hidden HPFS/NTFS",
-        0x18: "AST SmartSleep",
-        0x1b: "Hidden Win95 FAT32",
-        0x1c: "Hidden Win95 FAT32 (LBA)",
-        0x1e: "Hidden Win95 FAT16 (LBA)",
-        0x24: "NEC_DOS",
-        0x39: "Plan 9",
-        0x40: "Venix 80286",
-        0x41: "PPC_PReP Boot",
-        0x42: "SFS",
-        0x4d: "QNX4.x",
-        0x4e: "QNX4.x 2nd part",
-        0x4f: "QNX4.x 2nd part",
-        0x51: "Novell?",
-        0x52: "Microport",
-        0x63: "GNU HURD",
-        0x64: "Novell Netware 286",
-        0x65: "Novell Netware 386",
-        0x75: "PC/IX",
-        0x80: "Old MINIX",
-        0x81: "Linux/MINIX",
-        0x82: "Linux swap",
-        0x83: "Linux native",
-        0x84: "OS/2 hidden C:",
-        0x85: "Linux Extended",
-        0x86: "NTFS volume set",
-        0x87: "NTFS volume set",
-        0x8e: "Linux LVM",
-        0x93: "Amoeba",
-        0x94: "Amoeba BBT",
-        0x9f: "BSD/OS",
-        0xa0: "IBM Thinkpad hibernation",
-        0xa5: "BSD/386",
-        0xa6: "OpenBSD",
-        0xb7: "BSDI fs",
-        0xb8: "BSDI swap",
-        0xbf: "Solaris",
-        0xc7: "Syrinx",
-        0xdb: "CP/M",
-        0xde: "Dell Utility",
-        0xe1: "DOS access",
-        0xe3: "DOS R/O",
-        0xeb: "BEOS",
-        0xee: "EFI GPT",
-        0xef: "EFI (FAT-12/16/32)",
-        0xf2: "DOS secondary",
-        0xfd: "Linux RAID",
-        0xff: "BBT"
-}
+    0 : "Empty",
+    1: "DOS 12-bit FAT",
+    2: "XENIX root",
+    3: "XENIX usr",
+    4: "DOS 16-bit <32M",
+    5: "Extended",
+    6: "DOS 16-bit >=32M",
+    7: "NTFS/HPFS",
+    8: "AIX",
+    9: "AIX bootable",
+    10: "OS/2 Boot Manager",
+    0xb: "Win95 FAT32",
+    0xc: "Win95 FAT32",
+    0xe: "Win95 FAT16",
+    0xf: "Win95 Ext'd",
+    0x10: "OPUS",
+    0x11: "Hidden FAT12",
+    0x12: "Compaq Setup",
+    0x14: "Hidden FAT16 <32M",
+    0x16: "Hidden FAT16",
+    0x17: "Hidden HPFS/NTFS",
+    0x18: "AST SmartSleep",
+    0x1b: "Hidden Win95 FAT32",
+    0x1c: "Hidden Win95 FAT32 (LBA)",
+    0x1e: "Hidden Win95 FAT16 (LBA)",
+    0x24: "NEC_DOS",
+    0x39: "Plan 9",
+    0x40: "Venix 80286",
+    0x41: "PPC_PReP Boot",
+    0x42: "SFS",
+    0x4d: "QNX4.x",
+    0x4e: "QNX4.x 2nd part",
+    0x4f: "QNX4.x 2nd part",
+    0x51: "Novell?",
+    0x52: "Microport",
+    0x63: "GNU HURD",
+    0x64: "Novell Netware 286",
+    0x65: "Novell Netware 386",
+    0x75: "PC/IX",
+    0x80: "Old MINIX",
+    0x81: "Linux/MINIX",
+    0x82: "Linux swap",
+    0x83: "Linux native",
+    0x84: "OS/2 hidden C:",
+    0x85: "Linux Extended",
+    0x86: "NTFS volume set",
+    0x87: "NTFS volume set",
+    0x8e: "Linux LVM",
+    0x93: "Amoeba",
+    0x94: "Amoeba BBT",
+    0x9f: "BSD/OS",
+    0xa0: "IBM Thinkpad hibernation",
+    0xa5: "BSD/386",
+    0xa6: "OpenBSD",
+    0xb7: "BSDI fs",
+    0xb8: "BSDI swap",
+    0xbf: "Solaris",
+    0xc7: "Syrinx",
+    0xdb: "CP/M",
+    0xde: "Dell Utility",
+    0xe1: "DOS access",
+    0xe3: "DOS R/O",
+    0xeb: "BEOS",
+    0xee: "EFI GPT",
+    0xef: "EFI (FAT-12/16/32)",
+    0xf2: "DOS secondary",
+    0xfd: "Linux RAID",
+    0xff: "BBT"
+    }
 
 max_logical_partition_count = {
-        "hd": 59,
-        "sd": 11,
-        "ataraid/": 11,
-        "rd/": 3,
-        "cciss/": 11,
-        "i2o/": 11,
-        "iseries/vd": 3,
-        "ida/": 11,
-        "sx8/": 11,
-        "xvd": 11,
+    "hd": 59,
+    "sd": 11,
+    "ataraid/": 11,
+    "rd/": 3,
+    "cciss/": 11,
+    "i2o/": 11,
+    "iseries/vd": 3,
+    "ida/": 11,
+    "sx8/": 11,
+    "xvd": 11,
+    "vd": 11,
+    "mmcblk": 5
 }
index 5c730071fb34edc0a59f27c7432dc731c911fbf8..dc4fa5007f349dc43aa26c5927470b50e90a29ed 100644 (file)
@@ -18,7 +18,7 @@
 
 import isys
 import sys
-import inutil
+import iutil
 from constants import *
 from flags import flags
 from partErrors import *
@@ -59,7 +59,7 @@ def partitioningComplete(pomona):
             raise RuntimeError, ("Managed to not get an entry back from "
                                  "request.toEntry")
 
-    if inutil.memAvailable() > isys.EARLY_SWAP_RAM:
+    if iutil.memAvailable() > isys.EARLY_SWAP_RAM:
         return
 
     rc = pomona.intf.messageWindow(_("Low Memory"),
index 3a822790ad2329331d6d82dfb91963ef973f6069..afea78b611baebe11cfcf0141c7ab00f29f265c3 100644 (file)
@@ -1,33 +1,47 @@
 #
 # partitions.py: partition object containing partitioning info
 #
-# Matt Wilson <msw@redhat.com>
-# Jeremy Katz <katzj@redhat.com>
-# Mike Fulbright <msf@redhat.com>
-# Harald Hoyer <harald@redhat.de>
+# Copyright (C) 2002, 2003, 2004, 2005, 2006  Red Hat, Inc.
+# All rights reserved.
 #
-# Copyright 2002-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): Matt Wilson <msw@redhat.com>
+#            Jeremy Katz <katzj@redhat.com>
+#            Mike Fulbright <msf@redhat.com>
+#            Harald Hoyer <harald@redhat.de>
+#
+
 """Overarching partition object."""
 
 import parted
-import inutil
+import iutil
 import isys
 import string
-import os, sys
+import os
+import sys
 
 from constants import *
+from flags import flags
+from errors import *
 
 import fsset
+import raid
+import lvm
 import partedUtils
 import partRequests
+import cryptodev
 
 import gettext
 _ = lambda x: gettext.ldgettext("pomona", x)
@@ -35,13 +49,134 @@ _ = lambda x: gettext.ldgettext("pomona", x)
 import logging
 log = logging.getLogger("pomona")
 
+# dispatch.py helper function
+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()
+
+    if pomona.dir == DISPATCH_BACK:
+        return
+
+    # ensure zfcp devs are up
+    pomona.id.zfcp.startup()
+
+    # pull in the new iscsi drive
+    isys.flushDriveDict()
+
+    # read in drive info
+    pomona.id.diskset.refreshDevices()
+
+    pomona.id.partitions.setFromDisk(pomona.id.diskset)
+    pomona.id.partitions.setProtected(pomona.dispatch)
+
+# dispatch.py helper function
+def partitioningComplete(pomona):
+    if pomona.dir == DISPATCH_BACK and pomona.id.fsset.isActive():
+        rc = pomona.intf.messageWindow(_("Installation cannot continue."),
+                                _("The partitioning options you have chosen "
+                                  "have already been activated. You can "
+                                  "no longer return to the disk editing "
+                                  "screen. Would you like to continue "
+                                  "with the installation process?"),
+                                type = "yesno")
+        if rc == 0:
+            sys.exit(0)
+        return DISPATCH_FORWARD
+
+    pomona.id.partitions.sortRequests()
+    pomona.id.fsset.reset()
+    undoEncryption = False
+    partitions = pomona.id.partitions
+    preexist = partitions.hasPreexistingCryptoDev()
+    for request in pomona.id.partitions.requests:
+        # XXX improve sanity checking
+        if (not request.fstype or (request.fstype.isMountable()
+            and not request.mountpoint)):
+            continue
+
+        if request.encryption and request.encryption.format:
+            if pomona.isKickstart and request.passphrase:
+                # they set a passphrase for this device explicitly
+                pass
+            elif partitions.encryptionPassphrase:
+                request.encryption.setPassphrase(partitions.encryptionPassphrase)
+            elif undoEncryption:
+                request.encryption = None
+                if request.dev:
+                    request.dev.crypto = None
+            else:
+                while True:
+                    (passphrase, retrofit) = pomona.intf.getLuksPassphrase(preexist=preexist)
+                    if passphrase:
+                        request.encryption.setPassphrase(passphrase)
+                        partitions.encryptionPassphrase = passphrase
+                        partitions.retrofitPassphrase = retrofit
+                        break
+                    else:
+                        rc = pomona.intf.messageWindow(_("Encrypt device?"),
+                                    _("You specified block device encryption "
+                                      "should be enabled, but you have not "
+                                      "supplied a passphrase. If you do not "
+                                      "go back and provide a passphrase, "
+                                      "block device encryption will be "
+                                      "disabled."),
+                                      type="custom",
+                                      custom_buttons=[_("Back"), _("Continue")],
+                                      default=0)
+                        if rc == 1:
+                            log.info("user elected to not encrypt any devices.")
+                            request.encryption = None
+                            if request.dev:
+                                request.dev.encryption = None
+                            undoEncryption = True
+                            partitions.autoEncrypt = False
+                            break
+
+        entry = request.toEntry(pomona.id.partitions)
+        if entry:
+            pomona.id.fsset.add (entry)
+        else:
+            raise RuntimeError, ("Managed to not get an entry back from "
+                                 "request.toEntry")
+
+    if pomona.isKickstart:
+        return
+
+    rc = pomona.intf.messageWindow(_("Writing partitioning to disk"),
+                                _("The partitioning options you have selected "
+                                  "will now be written to disk.  Any "
+                                  "data on deleted or reformatted partitions "
+                                  "will be lost."),
+                                type = "custom", custom_icon="warning",
+                                custom_buttons=[_("Go _back"),
+                                                _("_Write changes to disk")],
+                                default = 0)
+    if rc == 0:
+        return DISPATCH_BACK
+
+def lookup_cryptodev(device):
+    for encryptedDev, cdev in Partitions.encryptedDevices.items():
+        mappedDev = cdev.getDevice()
+        if device == encryptedDev or device == mappedDev:
+            return cdev
+
 class Partitions:
     """Defines all of the partition requests and delete requests."""
-    def __init__ (self, diskset = None):
+    encryptedDevices = {}
+
+    def __init__ (self, pomona, readDisks=False):
         """Initializes a Partitions object.
 
         Can pass in the diskset if it already exists.
         """
+        self.pomona = pomona
+
         self.requests = []
         """A list of RequestSpec objects for all partitions."""
 
@@ -53,7 +188,7 @@ class Partitions:
         These are setup by the installclass and folded into self.requests
         by auto partitioning."""
 
-        self.autoClearPartType = CLEARPART_TYPE_ALL
+        self.autoClearPartType = CLEARPART_TYPE_NONE
         """What type of partitions should be cleared?"""
 
         self.autoClearPartDrives = None
@@ -68,19 +203,170 @@ class Partitions:
         self.zeroMbr = 0
         """Should the mbr be zero'd?"""
 
+        self.protected = []
+        """A list of partitions that are the installation source for hard
+           drive or livecd installs.  Partitions on this list may not be
+           formatted."""
+
+        self.autoEncrypt = False
+
+        self.encryptionPassphrase = ""
+        self.retrofitPassphrase = False
+
         # partition method to be used.  not to be touched externally
         self.useAutopartitioning = 1
         self.useFdisk = 0
 
-        if diskset:
-            diskset.refreshDevices()
-            self.setFromDisk(diskset)
+        # 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)
+
+    def protectedPartitions(self):
+        return self.protected
+
+    def hasPreexistingCryptoDev(self):
+        rc = False
+        for request in self.requests:
+            if request.encryption and request.encryption.format == 0:
+                rc = True
+                break
+
+        return rc
+
+    def getCryptoDev(self, device):
+        log.info("going to get passphrase for encrypted device %s" % device)
+        luksDev = self.encryptedDevices.get(device)
+        if luksDev:
+            log.debug("passphrase for device %s already known" % device)
+            return luksDev
+
+        intf = self.pomona.intf
+        luksDev = cryptodev.LUKSDevice(device)
+        if self.encryptionPassphrase:
+            luksDev.setPassphrase(self.encryptionPassphrase)
+            if not luksDev.openDevice():
+                self.encryptedDevices[device] = luksDev
+                return luksDev
+            else:
+                luksDev.setPassphrase("")
+
+        if intf is None:
+            return
+
+        buttons = [_("Back"), _("Continue")]
+        devname = os.path.basename(device)
+        while True:
+            (passphrase, isglobal) = intf.passphraseEntryWindow(devname)
+            if not passphrase:
+                rc = intf.messageWindow(_("Confirm"),
+                                        _("Are you sure you want to skip "
+                                          "entering a passphrase for device "
+                                          "%s?\n\n"
+                                          "If you skip this step the "
+                                          "device's contents will not "
+                                          "be available during "
+                                          "installation.") % devname,
+                                        type = "custom",
+                                        default = 0,
+                                        custom_buttons = buttons)
+                if rc == 0:
+                    continue
+                else:
+                    log.info("skipping passphrase for %s" % (device,))
+                    break
+
+            luksDev.setPassphrase(passphrase)
+            rc = luksDev.openDevice()
+            if rc:
+                luksDev.setPassphrase("")
+                continue
+            else:
+                self.encryptedDevices[device] = luksDev
+                if isglobal:
+                    self.encryptionPassphrase = passphrase
+                break
+
+        return self.encryptedDevices.get(device)
+
+    def getEncryptedDevices(self, diskset):
+        """ find and obtain passphrase for any encrypted devices """
+        drives = diskset.disks.keys()
+        drives.sort()
+        for drive in drives:
+            if diskset.pomona.isKickstart and \
+               ((self.autoClearPartType != CLEARPART_TYPE_NONE and \
+                 (not self.autoClearPartDrives or \
+                  drive in self.autoClearPartDrives)) or \
+                 drive in diskset.skippedDisks):
+                continue
+
+            disk = diskset.disks[drive]
+            part = disk.next_partition()
+            while part:
+                if part.type & parted.PARTITION_METADATA:
+                    part = disk.next_partition(part)
+                    continue
+
+                device = partedUtils.get_partition_name(part)
+                fs = partedUtils.sniffFilesystemType("/dev/%s" % (device,))
+                if fs and fs.endswith("raid"):
+                    part = disk.next_partition(part)
+                    continue
+
+                if cryptodev.isLuks("/dev/%s" % device):
+                    self.getCryptoDev(device)
+
+                part = disk.next_partition(part)
+
+        diskset.startMPath()
+        diskset.startDmRaid()
+        diskset.startMdRaid()
+        mdList = diskset.mdList
+        for raidDev in mdList:
+            (theDev, devices, level, numActive) = raidDev
+            if cryptodev.isLuks("/dev/%s" % theDev):
+                self.getCryptoDev(theDev)
+
+        lvm.writeForceConf()
+        # now to read in pre-existing LVM stuff
+        lvm.vgscan()
+        lvm.vgactivate()
+
+        for (vg, size, pesize, vgfree) in lvm.vglist():
+            for (lvvg, lv, size, lvorigin) in lvm.lvlist():
+                if lvorigin:
+                    continue
+                if lvvg != vg:
+                    continue
+
+                theDev = "/dev/mapper/%s-%s" %(vg, lv)
+                if cryptodev.isLuks(theDev):
+                    self.getCryptoDev("mapper/%s-%s" % (vg, lv))
+
+        lvm.vgdeactivate()
+        diskset.stopMdRaid()
+        for luksDev in self.encryptedDevices.values():
+            luksDev.closeDevice()
+        # try again now that encryption mappings are closed
+        lvm.vgdeactivate()
+        diskset.stopMdRaid()
+        for luksDev in self.encryptedDevices.values():
+            luksDev.closeDevice()
+
+        # We shouldn't have any further need for the global passphrase
+        # except for new device creation, in which case we want to give
+        # the user a chance to establish a new global passphrase.
+        self.encryptionPassphrase = ""
 
     def setFromDisk(self, diskset):
         """Clear the delete list and set self.requests to reflect disk."""
         self.deletes = []
         self.requests = []
-        labels = diskset.getLabels()
+        labels = diskset.getInfo()
         drives = diskset.disks.keys()
         drives.sort()
         for drive in drives:
@@ -103,11 +389,21 @@ class Partitions:
                 else:
                     ptype = partedUtils.get_partition_file_system_type(part)
 
-                # XXX FIXME: we don't handle ptype being None very well, so
-                # just say it's foreign.  Should probably fix None
-                # handling instead some day.
-                if ptype is None:
-                    ptype = fsset.fileSystemTypeGet("foreign")
+                    # FIXME: we don't handle ptype being None very well, so
+                    # just say it's foreign.  Should probably fix None
+                    # handling instead some day.
+                    if ptype is None:
+                        ptype = fsset.fileSystemTypeGet("foreign")
+
+                device = partedUtils.get_partition_name(part)
+                luksDev = self.encryptedDevices.get(device)
+                if luksDev and not luksDev.openDevice():
+                    mappedDev = luksDev.getDevice()
+                    fsname = partedUtils.sniffFilesystemType("/dev/%s" % mappedDev)
+                    try:
+                        ptype = fsset.fileSystemTypeGet(fsname)
+                    except:
+                        ptype = fsset.fileSystemTypeGet("foreign")
 
                 start = part.geom.start
                 end = part.geom.end
@@ -121,18 +417,176 @@ class Partitions:
                                                              drive = drive,
                                                              format = format)
                 spec.device = fsset.PartedPartitionDevice(part).getDevice()
+                spec.encryption = luksDev
+                spec.maxResizeSize = partedUtils.getMaxAvailPartSizeMB(part)
 
                 # set label if makes sense
-                if ptype and ptype.isMountable() and \
-                                (ptype.getName() == "ext2" or ptype.getName() == "ext3"):
+                if ptype and ptype.isMountable():
                     if spec.device in labels.keys():
                         if labels[spec.device] and len(labels[spec.device])>0:
                             spec.fslabel = labels[spec.device]
-
+                    elif luksDev and not luksDev.getStatus() and mappedDev in labels.keys():
+                        if labels[mappedDev] and len(labels[mappedDev])>0:
+                            spec.fslabel = labels[mappedDev]
                 self.addRequest(spec)
                 part = disk.next_partition(part)
 
-    def addRequest(self, request):
+        # now we need to read in all pre-existing RAID stuff
+        diskset.startMPath()
+        diskset.startDmRaid()
+        diskset.startMdRaid()
+        mdList = diskset.mdList
+        for raidDev in mdList:
+            (theDev, devices, level, numActive) = raidDev
+            level = "RAID%s" %(level,)
+
+            if level not in raid.availRaidLevels:
+                log.warning("raid level %s not supported, skipping %s" %(level,
+                                                                  theDev))
+                continue
+
+            try:
+                chunk = isys.getRaidChunkFromDevice("/dev/%s" %(devices[0],))
+            except Exception, e:
+                log.error("couldn't get chunksize of %s: %s" %(theDev, e))
+                chunk = None
+
+            # is minor always mdN ?
+            minor = int(theDev[2:])
+            raidvols = []
+            for dev in devices:
+                req = self.getRequestByDeviceName(dev)
+                if not req:
+                    log.error("RAID device %s using non-existent partition %s"
+                              %(theDev, dev))
+                    continue
+                raidvols.append(req.uniqueID)
+
+
+            luksDev = self.encryptedDevices.get(theDev)
+            if luksDev and not luksDev.openDevice():
+                device = luksDev.getDevice()
+            else:
+                device = theDev
+
+            fs = partedUtils.sniffFilesystemType("/dev/%s" %(device,))
+            try:
+                fsystem = fsset.fileSystemTypeGet(fs)
+            except:
+                fsystem = fsset.fileSystemTypeGet("foreign")
+
+            try:
+                fslabel = isys.readFSLabel(device)
+            except:
+                fslabel = None
+
+            mnt = None
+            format = 0
+
+            spares = len(devices) - numActive
+            spec = partRequests.RaidRequestSpec(fsystem, format = format,
+                                                raidlevel = level,
+                                                raidmembers = raidvols,
+                                                raidminor = minor,
+                                                raidspares = spares,
+                                                mountpoint = mnt,
+                                                preexist = 1,
+                                                chunksize = chunk,
+                                                fslabel = fslabel)
+            spec.size = spec.getActualSize(self, diskset)
+            spec.encryption = luksDev
+            self.addRequest(spec)
+
+        lvm.writeForceConf()
+        # now to read in pre-existing LVM stuff
+        lvm.vgscan()
+        lvm.vgactivate()
+
+        pvs = lvm.pvlist()
+        for (vg, size, pesize, vgfree) in lvm.vglist():
+            try:
+                preexist_size = float(size)
+            except:
+                log.error("preexisting size for %s not a valid integer, ignoring" %(vg,))
+                preexist_size = None
+
+            pvids = []
+            for (dev, pvvg, size) in pvs:
+                if vg != pvvg:
+                    continue
+                req = self.getRequestByDeviceName(dev[5:])
+                if not req:
+                    log.error("Volume group %s using non-existent partition %s"
+                              %(vg, dev))
+                    continue
+                pvids.append(req.uniqueID)
+            spec = partRequests.VolumeGroupRequestSpec(format = 0,
+                                                       vgname = vg,
+                                                       physvols = pvids,
+                                                       pesize = pesize,
+                                                       preexist = 1,
+                                                       preexist_size = preexist_size)
+            spec.free = vgfree
+            vgid = self.addRequest(spec)
+
+            for (lvvg, lv, size, lvorigin) in lvm.lvlist():
+                if lvorigin:
+                    continue
+                if lvvg != vg:
+                    continue
+
+                # size is number of bytes, we want size in megs
+                lvsize = float(size)
+
+                theDev = "/dev/%s/%s" %(vg, lv)
+
+                luksDev = self.encryptedDevices.get("mapper/%s-%s" % (vg, lv))
+                if luksDev and not luksDev.openDevice():
+                    device = luksDev.getDevice()
+                else:
+                    device = theDev
+
+                fs = partedUtils.sniffFilesystemType(device)
+                fslabel = None
+
+                try:
+                    fsystem = fsset.fileSystemTypeGet(fs)
+                except:
+                    fsystem = fsset.fileSystemTypeGet("foreign")
+
+                try:
+                    fslabel = isys.readFSLabel(device)
+                except:
+                    fslabel = None
+
+                mnt = None
+                format = 0
+
+                spec = partRequests.LogicalVolumeRequestSpec(fsystem,
+                    format = format, size = lvsize, volgroup = vgid,
+                    lvname = lv, mountpoint = mnt, fslabel = fslabel,
+                    preexist = 1)
+                if fsystem.isResizable():
+                    spec.minResizeSize = fsystem.getMinimumSize("%s/%s" %(vg, lv))
+                spec.encryption = luksDev
+                self.addRequest(spec)
+
+        for vg in lvm.partialvgs():
+            spec = partRequests.PartialVolumeGroupSpec(vgname = vg)
+            self.addDelete(spec)
+
+        lvm.vgdeactivate()
+        diskset.stopMdRaid()
+        for luksDev in self.encryptedDevices.values():
+            luksDev.closeDevice()
+
+        # try again now that encryption mappings are closed
+        lvm.vgdeactivate()
+        diskset.stopMdRaid()
+        for luksDev in self.encryptedDevices.values():
+            luksDev.closeDevice()
+
+    def addRequest (self, request):
         """Add a new request to the list."""
         if not request.uniqueID:
             request.uniqueID = self.nextUniqueID
@@ -142,12 +596,12 @@ class Partitions:
 
         return request.uniqueID
 
-    def addDelete(self, delete):
+    def addDelete (self, delete):
         """Add a new DeleteSpec to the list."""
         self.deletes.append(delete)
         self.deletes.sort()
 
-    def removeRequest(self, request):
+    def removeRequest (self, request):
         """Remove a request from the list."""
         self.requests.remove(request)
 
@@ -157,6 +611,9 @@ class Partitions:
             if request.mountpoint == mount:
                 return request
 
+        for request in self.requests:
+            if request.type == REQUEST_LV and request.mountpoint == mount:
+                return request
         return None
 
     def getRequestByDeviceName(self, device):
@@ -165,11 +622,22 @@ class Partitions:
             return None
 
         for request in self.requests:
-            if request.device == device:
+            if request.type == REQUEST_RAID and request.raidminor is not None:
+                tmp = "md%d" % (request.raidminor,)
+                if tmp == device:
+                    return request
+            elif request.device == device:
                 return request
-
+            elif request.encryption:
+                deviceUUID = cryptodev.luksUUID("/dev/" + device)
+                cryptoDev = request.encryption.getDevice()
+                cryptoUUID = request.encryption.getUUID()
+                if cryptoDev == device or \
+                   (cryptoUUID and cryptoUUID == deviceUUID):
+                    return request
         return None
 
+
     def getRequestsByDevice(self, diskset, device):
         """Find and return the requests on a given device (like 'hda')."""
         if device is None:
@@ -195,6 +663,27 @@ class Partitions:
         else:
             return None
 
+    def getRequestByVolumeGroupName(self, volname):
+        """Find and return the request with the given volume group name."""
+        if volname is None:
+            return None
+
+        for request in self.requests:
+            if (request.type == REQUEST_VG and
+                request.volumeGroupName == volname):
+                return request
+        return None
+
+    def getRequestByLogicalVolumeName(self, lvname):
+        """Find and return the request with the given logical volume name."""
+        if lvname is None:
+            return None
+        for request in self.requests:
+            if (request.type == REQUEST_LV and
+                request.logicalVolumeName == lvname):
+                return request
+        return None
+
     def getRequestByID(self, id):
         """Find and return the request with the given unique ID.
 
@@ -202,19 +691,265 @@ class Partitions:
         """
         if type(id) == type("a string"):
             id = int(id)
-            for request in self.requests:
-                if request.uniqueID == id:
-                    return request
-        else: ### XXX debug
-            log.debug("%s is not a string!" % (id,))
+        for request in self.requests:
+            if request.uniqueID == id:
+                return request
+        return None
+
+    def getRaidRequests(self):
+        """Find and return a list of all of the RAID requests."""
+        retval = []
+        for request in self.requests:
+            if request.type == REQUEST_RAID:
+                retval.append(request)
+
+        return retval
+
+    def getRaidDevices(self):
+        """Find and return a list of all of the requests for use in RAID."""
+        raidRequests = []
+        for request in self.requests:
+            if isinstance(request, partRequests.RaidRequestSpec):
+                raidRequests.append(request)
+
+        return raidRequests
+
+    def getAvailableRaidMinors(self):
+        """Find and return a list of all of the unused minors for use in RAID."""
+        raidMinors = range(0,32)
+        for request in self.requests:
+            if isinstance(request, partRequests.RaidRequestSpec) and request.raidminor in raidMinors:
+                raidMinors.remove(request.raidminor)
+
+        return raidMinors
+
+
+    def getAvailRaidPartitions(self, request, diskset):
+        """Return a list of tuples of RAID partitions which can be used.
+
+        Return value is (part, size, used) where used is 0 if not,
+        1 if so, 2 if used for *this* request.
+        """
+        rc = []
+        drives = diskset.disks.keys()
+        raiddevs = self.getRaidDevices()
+        drives.sort()
+        for drive in drives:
+            disk = diskset.disks[drive]
+            for part in partedUtils.get_raid_partitions(disk):
+                partname = partedUtils.get_partition_name(part)
+                used = 0
+                for raid in raiddevs:
+                    if raid.raidmembers:
+                        for raidmem in raid.raidmembers:
+                            tmpreq = self.getRequestByID(raidmem)
+                            if (partname == tmpreq.device):
+                                if raid.uniqueID == request.uniqueID:
+                                    used = 2
+                                else:
+                                    used = 1
+                                break
+                    if used:
+                        break
+                size = partedUtils.getPartSizeMB(part)
+
+                if not used:
+                    rc.append((partname, size, 0))
+                elif used == 2:
+                    rc.append((partname, size, 1))
+
+        return rc
+
+    def getRaidMemberParent(self, request):
+        """Return RAID device request containing this request."""
+        raiddev = self.getRaidRequests()
+        if not raiddev or not request.device:
+            return None
+        for dev in raiddev:
+            if not dev.raidmembers:
+                continue
+            for member in dev.raidmembers:
+                if request.device == self.getRequestByID(member).device:
+                    return dev
         return None
 
+    def isRaidMember(self, request):
+        """Return whether or not the request is being used in a RAID device."""
+        if self.getRaidMemberParent(request) is not None:
+            return 1
+        else:
+            return 0
+
+    def getLVMLVForVGID(self, vgid):
+        """Find and return a list of all the LVs associated with a VG id."""
+        retval = []
+        for request in self.requests:
+            if request.type == REQUEST_LV:
+                if request.volumeGroup == vgid:
+                    retval.append(request)
+        return retval
+
+    def getLVMLVForVG(self, vgrequest):
+        """Find and return a list of all of the LVs in the VG."""
+        vgid = vgrequest.uniqueID
+        return self.getLVMLVForVGID(vgid)
+
+    def getLVMRequests(self):
+        """Return a dictionary of all of the LVM bits.
+
+        The dictionary returned is of the form vgname: [ lvrequests ]
+        """
+        retval = {}
+        for request in self.requests:
+            if request.type == REQUEST_VG:
+                retval[request.volumeGroupName] = self.getLVMLVForVG(request)
+
+        return retval
+
+    def getPartialLVMRequests(self):
+        """Return a list of all of the partial volume groups names."""
+        retval = []
+        for request in self.deletes:
+            if isinstance(request, partRequests.PartialVolumeGroupSpec):
+                retval.append(request.volumeGroupName)
+
+        return retval
+
+    def getLVMVGRequests(self):
+        """Find and return a list of all of the volume groups."""
+        retval = []
+        for request in self.requests:
+            if request.type == REQUEST_VG:
+                retval.append(request)
+
+        return retval
+
+    def getLVMLVRequests(self):
+        """Find and return a list of all of the logical volumes."""
+        retval = []
+        for request in self.requests:
+            if request.type == REQUEST_LV:
+                retval.append(request)
+
+        return retval
+
+    def getAvailLVMPartitions(self, request, diskset):
+        """Return a list of tuples of PV partitions which can be used.
+
+        Return value is (part, size, used) where used is 0 if not,
+        1 if so, 2 if used for *this* request.
+        """
+        rc = []
+        drives = diskset.disks.keys()
+        drives.sort()
+        volgroups = self.getLVMVGRequests()
+        pvlist = lvm.pvlist()
+        for drive in drives:
+            disk = diskset.disks[drive]
+            for part in partedUtils.get_lvm_partitions(disk):
+                partname = partedUtils.get_partition_name(part)
+                partrequest = self.getRequestByDeviceName(partname)
+                used = 0
+                for volgroup in volgroups:
+                    if volgroup.physicalVolumes:
+                        if partrequest.uniqueID in volgroup.physicalVolumes:
+                            if (request and request.uniqueID and
+                                volgroup.uniqueID == request.uniqueID):
+                                used = 2
+                            else:
+                                used = 1
+
+                    if used:
+                        break
+                size = None
+                for pvpart, pvvg, pvsize in pvlist:
+                    if pvpart == "/dev/%s" % (partname,):
+                        size = pvsize
+                if size is None:
+                    # if we get here, there's no PV data in the partition,
+                    # so clamp the partition's size to 64M
+                    size = partedUtils.getPartSizeMB(part)
+                    size = lvm.clampPVSize(size, 65536)
+
+                if used == 0:
+                    rc.append((partrequest.uniqueID, size, 0))
+                elif used == 2:
+                    rc.append((partrequest.uniqueID, size, 1))
+
+        # now find available RAID devices
+        raiddev = self.getRaidRequests()
+        if raiddev:
+            raidcounter = 0
+            for dev in raiddev:
+                used = 0
+
+                if dev.fstype is None:
+                    continue
+                if dev.fstype.getName() != "physical volume (LVM)":
+                    continue
+
+                for volgroup in volgroups:
+                    if volgroup.physicalVolumes:
+                        if dev.uniqueID in volgroup.physicalVolumes:
+                            if (request and request.uniqueID and
+                                volgroup.uniqueID == request.uniqueID):
+                                used = 2
+                            else:
+                                used = 1
+
+                    if used:
+                        break
+
+                size = dev.getActualSize(self, diskset)
+
+                if used == 0:
+                    rc.append((dev.uniqueID, size, 0))
+                elif used == 2:
+                    rc.append((dev.uniqueID, size, 1))
+
+                raidcounter = raidcounter + 1
+        return rc
+
+    def getLVMVolumeGroupMemberParent(self, request):
+        """Return parent volume group of a physical volume"""
+        volgroups = self.getLVMVGRequests()
+        if not volgroups:
+            return None
+
+        for volgroup in volgroups:
+            if volgroup.physicalVolumes:
+                if request.uniqueID in volgroup.physicalVolumes:
+                    return volgroup
+
+        return None
+
+    def isLVMVolumeGroupMember(self, request):
+        """Return whether or not the request is being used in an LVM device."""
+        if self.getLVMVolumeGroupMemberParent(request) is None:
+            return 0
+        else:
+            return 1
+
+    def isVolumeGroupNameInUse(self, vgname):
+        """Return whether or not the requested volume group name is in use."""
+        if not vgname:
+            return None
+
+        lvmrequests = self.getLVMRequests()
+        if lvmrequests:
+            if vgname in lvmrequests.keys():
+                return 1
+
+        lvmrequests = self.getPartialLVMRequests()
+        if lvmrequests:
+            if vgname in lvmrequests:
+                return 1
+
+        return 0
+
     def getBootableRequest(self):
         """Return the name of the current 'boot' mount point."""
-        bootreq = None
-
         bootreq = self.getRequestByMountPoint("/boot")
-
         if not bootreq:
             bootreq = self.getRequestByMountPoint("/")
 
@@ -241,25 +976,52 @@ class Partitions:
             if bootreq == request:
                 return 1
 
+            if bootreq.type == REQUEST_RAID and \
+                   request.uniqueID in bootreq.raidmembers:
+                return 1
+
         return 0
 
     def sortRequests(self):
         """Resort the requests into allocation order."""
         n = 0
         while n < len(self.requests):
+            # Ignore LVM Volume Group and Logical Volume requests,
+            # since these are not related to allocating disk partitions
+            if (self.requests[n].type == REQUEST_VG or self.requests[n].type == REQUEST_LV):
+                n = n + 1
+                continue
+
             for request in self.requests:
-  # for sized requests, we want the larger ones first
-                if (request.size and self.requests[n].size and
-                                (request.size < self.requests[n].size)):
+                # Ignore LVM Volume Group and Logical Volume requests,
+                # since these are not related to allocating disk partitions
+                if (request.type == REQUEST_VG or request.type == REQUEST_LV):
+                    continue
+                # for raid requests, the only thing that matters for sorting
+                # is the raid device since ordering by size is mostly
+                # irrelevant.  this also keeps things more consistent
+                elif (request.type == REQUEST_RAID or
+                    self.requests[n].type == REQUEST_RAID):
+                    if (request.type == self.requests[n].type and
+                        (self.requests[n].raidminor != None) and
+                        ((request.raidminor is None) or
+                         request.raidminor > self.requests[n].raidminor)):
+                        tmp = self.requests[n]
+                        index = self.requests.index(request)
+                        self.requests[n] = request
+                        self.requests[index] = tmp
+                # for sized requests, we want the larger ones first
+                elif (request.size and self.requests[n].size and
+                    (request.size < self.requests[n].size)):
                     tmp = self.requests[n]
                     index = self.requests.index(request)
                     self.requests[n] = request
                     self.requests[index] = tmp
-  # for cylinder-based, sort by order on the drive
+                # for cylinder-based, sort by order on the drive
                 elif (request.start and self.requests[n].start and
-                                (request.drive == self.requests[n].drive) and
-                                (request.type == self.requests[n].type) and
-                                (request.start > self.requests[n].start)):
+                      (request.drive == self.requests[n].drive) and
+                      (request.type == self.requests[n].type) and
+                      (request.start > self.requests[n].start)):
                     tmp = self.requests[n]
                     index = self.requests.index(request)
                     self.requests[n] = request
@@ -267,13 +1029,12 @@ class Partitions:
                 # finally just use when they defined the partition so
                 # there's no randomness thrown in
                 elif (request.size and self.requests[n].size and
-                                (request.size == self.requests[n].size) and
-                                (request.uniqueID < self.requests[n].uniqueID)):
+                      (request.size == self.requests[n].size) and
+                      (request.uniqueID < self.requests[n].uniqueID)):
                     tmp = self.requests[n]
                     index = self.requests.index(request)
                     self.requests[n] = request
                     self.requests[index] = tmp
-
             n = n + 1
 
         tmp = self.getBootableRequest()
@@ -281,7 +1042,12 @@ class Partitions:
         boot = []
         if tmp:
             for req in tmp:
-                boot.append(req)
+                # if raid, we want all of the contents of the bootable raid
+                if req.type == REQUEST_RAID:
+                    for member in req.raidmembers:
+                        boot.append(self.getRequestByID(member))
+                else:
+                    boot.append(req)
 
         # remove the bootables from the request
         for bootable in boot:
@@ -317,22 +1083,22 @@ class Partitions:
                               "megabytes which is usually too small to "
                               "install %s.") % (name,))
 
-        bootreqs = self.getBootableRequest() or []
-        # FIXME: missing a check to ensure this is gpt.
-        for br in bootreqs:
-            dev = br.device
-            # simplified getDiskPart() for sata only
-            if dev[-2] in string.digits:
-                num = dev[-2:]
-            elif dev[-1] in string.digits:
-                num = dev[-1]
-            else:
-                continue # we should never get here, but you never know...
-            if int(num) > 4:
-                print dev, num
-                errors.append(_("Your boot partition isn't on one of "
-                                "the first four partitions and thus "
-                                "won't be bootable."))
+        def getBaseReqs(reqs):
+            n = 0
+            while not reduce(lambda x,y: x and (y.type not in [REQUEST_RAID, REQUEST_LV]),
+                             reqs, True) \
+                    and len(reqs) > n:
+                req = reqs[n]
+                if req.type == REQUEST_RAID:
+                    for id in req.raidmembers:
+                        reqs.append(self.getRequestByID(id))
+                    del reqs[n]
+                    continue
+                elif req.type == REQUEST_LV:
+                    del reqs[n]
+                    continue
+                n += 1
+            return reqs
 
         for (mount, size) in checkSizes:
             req = self.getRequestByMountPoint(mount)
@@ -342,7 +1108,7 @@ class Partitions:
                 warnings.append(_("Your %s partition is less than %s "
                                   "megabytes which is lower than recommended "
                                   "for a normal %s install.")
-                                % (mount, size, name))
+                                %(mount, size, name))
 
         foundSwap = 0
         swapSize = 0
@@ -360,20 +1126,21 @@ class Partitions:
                 rc = request.doMountPointLinuxFSChecks()
                 if rc:
                     errors.append(rc)
-                if rc:
-                    errors.append(rc)
-                if not hasattr(request,'drive'):
-                    continue
+                if isinstance(request, partRequests.RaidRequestSpec):
+                    rc = request.sanityCheckRaid(self)
+                    if rc:
+                        errors.append(rc)
+            if not hasattr(request,'drive'):
+                continue
             for x in request.drive or []:
                 if isys.driveUsesModule(x, ["usb-storage", "ub"]):
                     usesUSB = True
-                elif isys.driveUsesModule(x, ["sbp2"]):
+                elif isys.driveUsesModule(x, ["sbp2", "firewire-sbp2"]):
                     usesFireWire = True
 
         if usesUSB:
             warnings.append(_("Installing on a USB device.  This may "
                               "or may not produce a working system."))
-
         if usesFireWire:
             warnings.append(_("Installing on a FireWire device.  This may "
                               "or may not produce a working system."))
@@ -381,11 +1148,38 @@ class Partitions:
         bootreqs = self.getBootableRequest()
         if bootreqs:
             for bootreq in bootreqs:
-                # XFS causes all kinds of disasters for being /boot.
-                # disallow it. #138673 and others.
-                if (bootreq and bootreq.fstype and
-                                bootreq.fstype.getName() == "xfs"):
-                    errors.append("Bootable partitions cannot be on an XFS filesystem.")
+                if not bootreq:
+                    continue
+                if (isinstance(bootreq, partRequests.RaidRequestSpec) and
+                    not raid.isRaid1(bootreq.raidlevel)):
+                    errors.append(_("Bootable partitions can only be on RAID1 "
+                                    "devices."))
+
+                # can't have bootable partition on LV
+                if (isinstance(bootreq, partRequests.LogicalVolumeRequestSpec)):
+                    errors.append(_("Bootable partitions cannot be on a "
+                                    "logical volume."))
+
+                # most arches can't have boot on RAID
+                if (isinstance(bootreq, partRequests.RaidRequestSpec) and
+                    iutil.getArch() not in raid.raidBootArches):
+                    errors.append(_("Bootable partitions cannot be on a RAID "
+                                    "device."))
+
+                # Lots of filesystems types don't support /boot.
+                if (bootreq.fstype and not bootreq.fstype.isBootable()):
+                    errors.append(_("Bootable partitions cannot be on an %s "
+                                    "filesystem.")%(bootreq.fstype.getName(),))
+
+                # vfat /boot is insane.
+                if (bootreq.mountpoint and bootreq.mountpoint == "/" and
+                    bootreq.fstype and bootreq.fstype.getName() == "vfat"):
+                    errors.append(_("Bootable partitions cannot be on an %s "
+                                    "filesystem.")%(bootreq.fstype.getName(),))
+
+                if (bootreq.isEncrypted(self)):
+                    errors.append(_("Bootable partitions cannot be on an "
+                                    "encrypted block device"))
 
         if foundSwap == 0:
             warnings.append(_("You have not specified a swap partition.  "
@@ -399,7 +1193,7 @@ class Partitions:
                               "The kernel for %s only supports 32 "
                               "swap devices.") % (name,))
 
-        mem = inutil.memInstalled()
+        mem = iutil.memInstalled()
         rem = mem % 16384
         if rem:
             mem = mem + (16384 - rem)
@@ -409,7 +1203,7 @@ class Partitions:
             warnings.append(_("You have allocated less swap space (%dM) than "
                               "available RAM (%dM) on your system.  This "
                               "could negatively impact performance.")
-                            % (swapSize, mem))
+                            %(swapSize, mem))
 
         if warnings == []:
             warnings = None
@@ -418,9 +1212,19 @@ class Partitions:
 
         return (errors, warnings)
 
-    def copy(self):
+    def setProtected(self, dispatch):
+        """Set any partitions which should be protected to be so."""
+        for device in self.protectedPartitions():
+            log.info("%s is a protected partition" % (device))
+            request = self.getRequestByDeviceName(device)
+            if request is not None:
+                request.setProtected(1)
+            else:
+                log.info("no request, probably a removable drive")
+
+    def copy (self):
         """Deep copy the object."""
-        new = Partitions()
+        new = Partitions(self.pomona)
         for request in self.requests:
             new.addRequest(request)
         for delete in self.deletes:
@@ -432,6 +1236,7 @@ class Partitions:
         new.useAutopartitioning = self.useAutopartitioning
         new.useFdisk = self.useFdisk
         new.reinitializeDisks = self.reinitializeDisks
+        new.protected = self.protected
         return new
 
     def getClearPart(self):
@@ -451,6 +1256,224 @@ class Partitions:
 
         return "#clearpart %s\n" %(string.join(clearpartargs))
 
+    def writeKS(self, f):
+        """Write out the partitioning information in kickstart format."""
+        f.write("# The following is the partition information you requested\n")
+        f.write("# Note that any partitions you deleted are not expressed\n")
+        f.write("# here so unless you clear all partitions first, this is\n")
+        f.write("# not guaranteed to work\n")
+        clearpart = self.getClearPart()
+        if clearpart:
+            f.write(clearpart)
+
+        # lots of passes here -- parts, raid, volgroup, logvol
+        # XXX what do we do with deleted partitions?
+        for request in self.requests:
+            args = []
+            if request.type == REQUEST_RAID:
+                continue
+
+            # no fstype, no deal (same with foreigns)
+            if not request.fstype or request.fstype.getName() == "foreign":
+                continue
+
+            # first argument is mountpoint, which can also be swap or
+            # the unique RAID identifier.  I hate kickstart partitioning
+            # syntax.  a lot.  too many special cases
+            if request.fstype.getName() == "swap":
+                args.append("swap")
+            elif request.fstype.getName() == "software RAID":
+                # since we guarantee that uniqueIDs are ints now...
+                args.append("raid.%s" % (request.uniqueID))
+            elif request.fstype.getName() == "physical volume (LVM)":
+                # see above about uniqueIDs being ints
+                args.append("pv.%s" % (request.uniqueID))
+            elif request.fstype.getName() == "PPC PReP Boot":
+                args.extend(["prepboot", "--fstype", "\"PPC PReP Boot\""])
+            elif request.fstype.getName() == "Apple Bootstrap":
+                args.extend(["appleboot", "--fstype", "\"Apple Bootstrap\""])
+            elif request.mountpoint:
+                args.extend([request.mountpoint, "--fstype",
+                             request.fstype.getName(quoted = 1)])
+            else:
+                continue
+
+            # generic options
+            if not request.format:
+                args.append("--noformat")
+
+            # device encryption
+            if request.encryption:
+                args.append("--encrypted")
+
+            # preexisting only
+            if request.type == REQUEST_PREEXIST and request.device:
+                args.append("--onpart")
+                args.append(request.device)
+            # we have a billion ways to specify new partitions
+            elif request.type == REQUEST_NEW:
+                if request.size:
+                    args.append("--size=%s" % (int(request.size),))
+                if request.size == 0:
+                    args.append("--size=0")
+                if request.grow:
+                    args.append("--grow")
+                if request.start:
+                    args.append("--start=%s" % (request.start))
+                if request.end:
+                    args.append("--end=%s" % (request.end))
+                if request.maxSizeMB:
+                    args.append("--maxsize=%s" % (request.maxSizeMB))
+                if request.drive:
+                    args.append("--ondisk=%s" % (request.drive[0]))
+                if request.primary:
+                    args.append("--asprimary")
+            else: # how the hell did we get this?
+                continue
+
+            f.write("#part %s\n" % (string.join(args)))
+
+
+        for request in self.requests:
+            args = []
+            if request.type != REQUEST_RAID:
+                continue
+
+            # no fstype, no deal (same with foreigns)
+            if not request.fstype or request.fstype.getName() == "foreign":
+                continue
+
+            # also require a raidlevel and raidmembers for raid
+            if (request.raidlevel == None) or not request.raidmembers:
+                continue
+
+            # first argument is mountpoint, which can also be swap
+            if request.fstype.getName() == "swap":
+                args.append("swap")
+            elif request.fstype.getName() == "physical volume (LVM)":
+                # see above about uniqueIDs being ints
+                args.append("pv.%s" % (request.uniqueID))
+            elif request.mountpoint:
+                args.append(request.mountpoint)
+            else:
+                continue
+
+            # generic options
+            if not request.format:
+                args.append("--noformat")
+            if request.preexist:
+                args.append("--useexisting")
+            if request.fstype:
+                args.extend(["--fstype", request.fstype.getName(quoted = 1)])
+
+            # device encryption
+            if request.encryption:
+                args.append("--encrypted")
+
+            args.append("--level=%s" % (request.raidlevel))
+            args.append("--device=md%s" % (request.raidminor))
+
+            if request.raidspares:
+                args.append("--spares=%s" % (request.raidspares))
+
+            # silly raid member syntax
+            raidmems = []
+            for member in request.raidmembers:
+                if (type(member) != type("")) or (member[0:5] != "raid."):
+                    raidmems.append("raid.%s" % (member))
+                else:
+                    raidmems.append(member)
+            args.append("%s" % (string.join(raidmems)))
+
+            f.write("#raid %s\n" % (string.join(args)))
+
+        for request in self.requests:
+            args = []
+            if request.type != REQUEST_VG:
+                continue
+
+            args.append(request.volumeGroupName)
+
+            # generic options
+            if not request.format:
+                args.append("--noformat")
+            if request.preexist:
+                args.append("--useexisting")
+
+            args.append("--pesize=%s" %(request.pesize,))
+
+            # silly pv syntax
+            pvs = []
+            for member in request.physicalVolumes:
+                if (type(member) != type("")) or not member.startswith("pv."):
+                    pvs.append("pv.%s" % (member))
+                else:
+                    pvs.append(member)
+            args.append("%s" % (string.join(pvs)))
+
+            f.write("#volgroup %s\n" % (string.join(args)))
+
+        for request in self.requests:
+            args = []
+            if request.type != REQUEST_LV:
+                continue
+
+            # no fstype, no deal (same with foreigns)
+            if not request.fstype or request.fstype.getName() == "foreign":
+                continue
+
+            # require a vg name and an lv name
+            if (request.logicalVolumeName is None or
+                request.volumeGroup is None):
+                continue
+
+            # first argument is mountpoint, which can also be swap
+            if request.fstype.getName() == "swap":
+                args.append("swap")
+            elif request.mountpoint:
+                args.append(request.mountpoint)
+            else:
+                continue
+
+            # generic options
+            if not request.format:
+                args.append("--noformat")
+            if request.preexist:
+                args.append("--useexisting")
+            if request.fstype:
+                args.extend(["--fstype", request.fstype.getName(quoted = 1)])
+
+            # device encryption
+            if request.encryption:
+                args.append("--encrypted")
+
+            vg = self.getRequestByID(request.volumeGroup)
+            if vg is None:
+                continue
+
+            args.extend(["--name=%s" %(request.logicalVolumeName,),
+                         "--vgname=%s" %(vg.volumeGroupName,)])
+
+            if request.grow:
+                if request.startSize:
+                    args.append("--size=%s" % (int(request.startSize),))
+                else:
+                    # shouldnt happen
+                    continue
+
+                args.append("--grow")
+                if request.maxSizeMB and int(request.maxSizeMB) > 0:
+                    args.append("--maxsize=%s" % (request.maxSizeMB,))
+            else:
+                if request.percent:
+                    args.append("--percent=%s" %(request.percent,))
+                elif request.size:
+                    args.append("--size=%s" %(int(request.size),))
+                else:
+                    continue
+
+            f.write("#logvol %s\n" % (string.join(args)))
+
     def deleteAllLogicalPartitions(self, part):
         """Add delete specs for all logical partitions in part."""
         for partition in partedUtils.get_logical_partitions(part.disk):
@@ -465,7 +1488,7 @@ class Partitions:
 
     def containsImmutablePart(self, part):
         """Returns whether the partition contains parts we can't delete."""
-        if not part or (type(part) == type(1)):
+        if not part or (type(part) == type("RAID")) or (type(part) == type(1)):
             return None
 
         if not part.type & parted.PARTITION_EXTENDED:
@@ -484,6 +1507,175 @@ class Partitions:
                 if request.getProtected():
                     return _("the partition in use by the installer.")
 
-            part = disk.next_partition(part)
+                if self.isRaidMember(request):
+                    return _("a partition which is a member of a RAID array.")
+
+                if self.isLVMVolumeGroupMember(request):
+                    return _("a partition which is a member of a LVM Volume Group.")
 
+            part = disk.next_partition(part)
         return None
+
+
+    def doMetaDeletes(self, diskset):
+        """Does the removal of all of the non-physical volumes in the delete list."""
+
+        # have to have lvm on, which requires raid to be started
+        diskset.startMPath()
+        diskset.startDmRaid()
+        diskset.startMdRaid()
+        for luksDev in self.encryptedDevices.values():
+            luksDev.openDevice()
+        lvm.vgactivate()
+
+        snapshots = {}
+        for (lvvg, lv, size, lvorigin) in lvm.lvlist():
+            snapshots.setdefault(lv, [])
+            if lvorigin:
+                snapshots.setdefault(lvorigin, [])
+                snapshots[lvorigin].append((lv, lvvg))
+
+        lvm_parent_deletes = []
+        tmp = {}
+        def addSnap(name, vg):
+            snaps = snapshots[name]
+            for snap, snapvg in snaps:
+                addSnap(snap, snapvg)
+            if not tmp.has_key((name, vg)):
+                tmp[(name, vg)] = 1
+                lvm_parent_deletes.append((name,vg))
+
+        # now, go through and delete logical volumes
+        for delete in self.deletes:
+            if isinstance(delete, partRequests.DeleteLogicalVolumeSpec):
+                if not delete.beenDeleted():
+                    addSnap(delete.name, delete.vg)
+                    delete.setDeleted(1)
+
+        for name,vg in lvm_parent_deletes:
+            log.info("removing lv %s" % (name,))
+            key = "mapper/%s-%s" % (vg, name)
+            if key in self.encryptedDevices.keys():
+                self.encryptedDevices[key].closeDevice()
+                del self.encryptedDevices[key]
+            lvm.lvremove(name, vg)
+
+        # now, go through and delete volume groups
+        for delete in self.deletes:
+            if isinstance(delete, partRequests.DeleteVolumeGroupSpec):
+                if not delete.beenDeleted():
+                    lvm.vgremove(delete.name)
+                    delete.setDeleted(1)
+
+        lvm.vgdeactivate()
+
+        # now, remove obsolete cryptodev instances
+        for (device, luksDev) in self.encryptedDevices.items():
+            luksDev.closeDevice()
+            found = 0
+            for req in self.requests:
+                if req.encryption == luksDev:
+                    found = 1
+
+            if not found:
+                del self.encryptedDevices[device]
+
+        diskset.stopMdRaid()
+
+    def doMetaResizes(self, diskset):
+        """Does resizing of non-physical volumes."""
+
+        # have to have lvm on, which requires raid to be started
+        devicesActive = diskset.devicesOpen
+        if not devicesActive:
+            # should this not be diskset.openDevices() ?
+            diskset.startMPath()
+            diskset.startDmRaid()
+            diskset.startMdRaid()
+
+        for luksDev in self.encryptedDevices.values():
+            luksDev.openDevice()
+        lvm.vgactivate()
+
+        # we only support resizing LVM of these types of things currently
+        for lv in self.getLVMLVRequests():
+            if not lv.preexist:
+                continue
+            if lv.targetSize is None:
+                continue
+
+            vg = self.getRequestByID(lv.volumeGroup)
+            if vg is None:
+                continue
+
+            lvm.lvresize(lv.logicalVolumeName, vg.volumeGroupName,
+                         lv.targetSize)
+
+        lvm.vgdeactivate()
+        for luksDev in self.encryptedDevices.values():
+            luksDev.closeDevice()
+
+        if not devicesActive:
+            # should this not be diskset.closeDevices() ?
+            diskset.stopMdRaid()
+            diskset.stopDmRaid()
+            diskset.stopMPath()
+
+    def doEncryptionRetrofits(self):
+        if not self.retrofitPassphrase or not self.encryptionPassphrase:
+            return
+
+        for request in self.requests:
+            if not request.encryption:
+                continue
+
+            # XXX this will only work before the new LUKS devices are created
+            #     since the format flag gets unset when they are formatted
+            if request.encryption.format:
+                continue
+
+            if request.encryption.addPassphrase(self.encryptionPassphrase):
+                log.error("failed to add new passphrase to existing device %s" % (request.encryption.getDevice(encrypted=1),))
+
+    def deleteDependentRequests(self, request):
+        """Handle deletion of this request and all requests which depend on it.
+
+        eg, delete all logical volumes from a volume group, all volume groups
+        which depend on the raid device.
+
+        Side effects: removes all dependent requests from self.requests
+                      adds needed dependent deletes to self.deletes
+        """
+
+        toRemove = []
+        id = request.uniqueID
+        for req in self.requests:
+            if isinstance(req, partRequests.RaidRequestSpec):
+                if id in req.raidmembers:
+                    toRemove.append(req)
+                # XXX do we need to do anything special with preexisting raids?
+            elif isinstance(req, partRequests.VolumeGroupRequestSpec):
+                if id in req.physicalVolumes:
+                    toRemove.append(req)
+                    if req.getPreExisting():
+                        delete = partRequests.DeleteVolumeGroupSpec(req.volumeGroupName)
+                        self.addDelete(delete)
+            elif isinstance(req, partRequests.LogicalVolumeRequestSpec):
+                if id == req.volumeGroup:
+                    toRemove.append(req)
+                    tmp = self.getRequestByID(req.volumeGroup)
+                    if not tmp:
+                        log.error("Unable to find the vg for %s"
+                                  % (req.logicalVolumeName,))
+                        vgname = req.volumeGroup
+                    else:
+                        vgname = tmp.volumeGroupName
+
+                    if req.getPreExisting():
+                        delete = partRequests.DeleteLogicalVolumeSpec(req.logicalVolumeName,
+                                                                      vgname)
+                        self.addDelete(delete)
+
+        for req in toRemove:
+            self.deleteDependentRequests(req)
+            self.removeRequest(req)
index 1db781c29910f8ed377e0a735ffd6b939299dfb6..d1757a55cd7b3ae1e3d4205c097ce7be05c6fb48 100644 (file)
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2009-01-29 01:02+0100\n"
+"POT-Creation-Date: 2009-02-17 19:28+0100\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -16,31 +16,32 @@ msgstr ""
 "Content-Type: text/plain; charset=CHARSET\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: ../autopart.py:751
+#: ../autopart.py:872
 #, python-format
 msgid ""
-"Could not allocate cylinder-based partitions as primary partitions.\n"
+"Error resizing partition %s.\n"
 "\n"
 "%s"
 msgstr ""
 
-#: ../autopart.py:756
+#: ../autopart.py:875
 #, python-format
-msgid ""
-"Could not allocate partitions as primary partitions.\n"
-"\n"
-"%s"
+msgid "Start of partition %s was moved when resizing"
 msgstr ""
 
-#: ../autopart.py:761
-#, python-format
-msgid ""
-"Could not allocate cylinder-based partitions.\n"
-"\n"
-"%s"
+#: ../autopart.py:967
+msgid "Could not allocate cylinder-based partitions as primary partitions.\n"
 msgstr ""
 
-#: ../autopart.py:800
+#: ../autopart.py:972
+msgid "Could not allocate partitions as primary partitions.\n"
+msgstr ""
+
+#: ../autopart.py:977
+msgid "Could not allocate cylinder-based partitions.\n"
+msgstr ""
+
+#: ../autopart.py:1042
 #, python-format
 msgid ""
 "Boot partition %s doesn't belong to a BSD disk label. SRM won't be able to "
@@ -48,7 +49,7 @@ msgid ""
 "change this device disk label to BSD."
 msgstr ""
 
-#: ../autopart.py:802
+#: ../autopart.py:1044
 #, python-format
 msgid ""
 "Boot partition %s doesn't belong to a disk with enough free space at its "
@@ -56,56 +57,106 @@ msgid ""
 "of free space at the beginning of the disk that contains /boot"
 msgstr ""
 
-#: ../autopart.py:804
+#: ../autopart.py:1046
 #, python-format
 msgid ""
 "Boot partition %s isn't a VFAT partition.  EFI won't be able to boot from "
 "this partition."
 msgstr ""
 
-#: ../autopart.py:806
+#: ../autopart.py:1048
 msgid ""
 "The boot partition must entirely be in the first 4GB of the disk.  "
 "OpenFirmware won't be able to boot this installation."
 msgstr ""
 
-#: ../autopart.py:813
+#: ../autopart.py:1050
+#, python-format
+msgid ""
+"Boot partition %s is not a Linux filesystem, such as ext3.  The system won't "
+"be able to boot from this partition."
+msgstr ""
+
+#: ../autopart.py:1053
 #, python-format
 msgid ""
 "Boot partition %s may not meet booting constraints for your architecture."
 msgstr ""
 
-#: ../autopart.py:951
+#: ../autopart.py:1078
+#, python-format
+msgid ""
+"Adding this partition would not leave enough disk space for already "
+"allocated logical volumes in %s."
+msgstr ""
+
+#: ../autopart.py:1231
 msgid "Requested Partition Does Not Exist"
 msgstr ""
 
-#: ../autopart.py:952
+#: ../autopart.py:1232
 #, python-format
 msgid ""
 "Unable to locate partition %s to use for %s.\n"
 "\n"
-"Press 'OK' to reboot your system."
+"Press 'OK' to exit the installer."
+msgstr ""
+
+#: ../autopart.py:1257
+msgid "Requested Raid Device Does Not Exist"
+msgstr ""
+
+#: ../autopart.py:1258
+#, python-format
+msgid ""
+"Unable to locate raid device %s to use for %s.\n"
+"\n"
+"Press 'OK' to exit the installer."
+msgstr ""
+
+#: ../autopart.py:1287
+msgid "Requested Volume Group Does Not Exist"
+msgstr ""
+
+#: ../autopart.py:1288
+#, python-format
+msgid ""
+"Unable to locate volume group %s to use for %s.\n"
+"\n"
+"Press 'OK' to exit the installer."
+msgstr ""
+
+#: ../autopart.py:1325
+msgid "Requested Logical Volume Does Not Exist"
+msgstr ""
+
+#: ../autopart.py:1326
+#, python-format
+msgid ""
+"Unable to locate logical volume %s to use for %s.\n"
+"\n"
+"Press 'OK' to exit the installer."
 msgstr ""
 
-#: ../autopart.py:997 ../autopart.py:1029
+#: ../autopart.py:1460 ../autopart.py:1507
 msgid "Automatic Partitioning Errors"
 msgstr ""
 
-#: ../autopart.py:998
+#: ../autopart.py:1461
 #, python-format
 msgid ""
 "The following errors occurred with your partitioning:\n"
 "\n"
 "%s\n"
 "\n"
-"Press 'OK' to reboot your system."
+"Press 'OK' to exit the installer."
 msgstr ""
 
-#: ../autopart.py:1007
+#: ../autopart.py:1471
 msgid "Warnings During Automatic Partitioning"
 msgstr ""
 
-#: ../autopart.py:1008
+#: ../autopart.py:1472
 #, python-format
 msgid ""
 "Following warnings occurred during automatic partitioning:\n"
@@ -113,26 +164,33 @@ msgid ""
 "%s"
 msgstr ""
 
-#: ../autopart.py:1016 ../tui_partition.py:177
+#: ../autopart.py:1486 ../autopart.py:1503
+msgid ""
+"\n"
+"\n"
+"Press 'OK' to exit the installer."
+msgstr ""
+
+#: ../autopart.py:1487 ../tui_partition.py:177
 msgid "Error Partitioning"
 msgstr ""
 
-#: ../autopart.py:1017
+#: ../autopart.py:1488
 #, python-format
 msgid ""
 "Could not allocate requested partitions: \n"
 "\n"
-"%s."
+"%s.%s"
 msgstr ""
 
-#: ../autopart.py:1027
+#: ../autopart.py:1505
 msgid ""
 "\n"
 "\n"
 "Press 'OK' to choose a different partitioning option."
 msgstr ""
 
-#: ../autopart.py:1030
+#: ../autopart.py:1508
 #, python-format
 msgid ""
 "The following errors occurred with your partitioning:\n"
@@ -143,13 +201,15 @@ msgid ""
 "installation. %s"
 msgstr ""
 
-#: ../autopart.py:1105 ../bootloader.py:745 ../partedUtils.py:230
-#: ../partedUtils.py:531 ../partedUtils.py:568 ../tui_bootloader.py:119
-#: ../tui_bootloader.py:439 ../tui_partition.py:182
-msgid "Warning"
+#: ../autopart.py:1519
+msgid "Unrecoverable Error"
+msgstr ""
+
+#: ../autopart.py:1520
+msgid "Your system will now be rebooted."
 msgstr ""
 
-#: ../autopart.py:1126
+#: ../autopart.py:1623
 msgid ""
 "Automatic Partitioning sets partitions based on the selected installation "
 "type. You also can customize the partitions once they have been created.\n"
@@ -159,97 +219,111 @@ msgid ""
 "mount points, partition sizes, and more."
 msgstr ""
 
-#: ../autopart.py:1137
+#: ../autopart.py:1634
 msgid ""
 "Before automatic partitioning can be set up by the installation program, you "
 "must choose how to use the space on your hard drives."
 msgstr ""
 
-#: ../autopart.py:1142
+#: ../autopart.py:1639
 msgid "Remove all partitions on this system"
 msgstr ""
 
-#: ../autopart.py:1144
-#, python-format
-msgid ""
-"You have chosen to remove all partitions (ALL DATA) on the following drives:%"
-"s\n"
-"Are you sure you want to do this?"
+#: ../autopart.py:1640
+msgid "Remove all Linux partitions on this system"
+msgstr ""
+
+#: ../autopart.py:1641
+msgid "Keep all partitions and use existing free space"
 msgstr ""
 
-#: ../bootloader.py:696
+#: ../bootloader.py:697
 msgid "Bootloader"
 msgstr ""
 
-#: ../bootloader.py:696
+#: ../bootloader.py:697
 msgid "Installing bootloader..."
 msgstr ""
 
-#: ../bootloader.py:746
+#: ../bootloader.py:746 ../partedUtils.py:328 ../partedUtils.py:851
+#: ../partedUtils.py:979 ../tui_bootloader.py:119 ../tui_bootloader.py:439
+#: ../tui_partition.py:182
+msgid "Warning"
+msgstr ""
+
+#: ../bootloader.py:747
 msgid ""
 "No kernel packages were installed on your system.  Your boot loader "
 "configuration will not be changed."
 msgstr ""
 
-#: ../constants.py:41
-#, python-format
+#: ../constants.py:61
 msgid ""
 "An unhandled exception has occurred.  This is most likely a bug.  Please "
-"save a copy of the detailed exception and file a bug report against pomona "
-"at %s"
+"save a copy of the detailed exception and file a bug report"
+msgstr ""
+
+#: ../constants.py:67
+msgid " with the provider of this software."
+msgstr ""
+
+#: ../constants.py:71
+#, python-format
+msgid " against pomona at %s"
 msgstr ""
 
-#: ../constants.py:83 ../installer.py:93 ../tui_confirm.py:28
+#: ../constants.py:89 ../installer.py:93 ../tui_confirm.py:28
 #: ../tui_network.py:65 ../tui_network.py:74 ../tui.py:224 ../tui.py:390
 msgid "OK"
 msgstr ""
 
-#: ../constants.py:87 ../partIntfHelpers.py:150 ../partIntfHelpers.py:392
+#: ../constants.py:93 ../partIntfHelpers.py:245 ../partIntfHelpers.py:536
 #: ../tui_bootloader.py:194 ../tui.py:103 ../tui.py:104 ../tui.py:393
 msgid "Cancel"
 msgstr ""
 
-#: ../constants.py:91 ../tui_confirm.py:28 ../tui_confirm.py:30
+#: ../constants.py:97 ../partitions.py:130 ../partitions.py:260
+#: ../tui_confirm.py:28 ../tui_confirm.py:30
 msgid "Back"
 msgstr ""
 
-#: ../constants.py:95 ../tui_bootloader.py:67 ../tui.py:388
+#: ../constants.py:101 ../tui_bootloader.py:67 ../tui.py:388
 msgid "Yes"
 msgstr ""
 
-#: ../constants.py:99 ../tui_bootloader.py:67 ../tui.py:389
+#: ../constants.py:105 ../tui_bootloader.py:67 ../tui.py:389
 msgid "No"
 msgstr ""
 
-#: ../constants.py:103 ../tui_bootloader.py:270 ../tui_partition.py:849
+#: ../constants.py:109 ../tui_bootloader.py:270 ../tui_partition.py:849
 msgid "Edit"
 msgstr ""
 
-#: ../fsset.py:422
+#: ../fsset.py:497
 msgid "Checking"
 msgstr ""
 
-#: ../fsset.py:423
+#: ../fsset.py:498
 #, python-format
 msgid "Checking filesystem on %s..."
 msgstr ""
 
-#: ../fsset.py:434
+#: ../fsset.py:509 ../fsset.py:944
 msgid "Resizing"
 msgstr ""
 
-#: ../fsset.py:435
+#: ../fsset.py:510 ../fsset.py:945
 #, python-format
 msgid "Resizing filesystem on %s..."
 msgstr ""
 
-#: ../fsset.py:578 ../fsset.py:1112 ../fsset.py:1143 ../fsset.py:1205
-#: ../fsset.py:1216 ../fsset.py:1270 ../fsset.py:1281 ../fsset.py:1303
-#: ../fsset.py:1352 ../fsset.py:1433 ../partIntfHelpers.py:289
+#: ../fsset.py:653 ../fsset.py:1536 ../fsset.py:1567 ../fsset.py:1643
+#: ../fsset.py:1711 ../fsset.py:1761 ../fsset.py:1850 ../fsset.py:1863
+#: ../partIntfHelpers.py:413
 msgid "Error"
 msgstr ""
 
-#: ../fsset.py:579
+#: ../fsset.py:654
 #, python-format
 msgid ""
 "An error occurred migrating %s to ext3.  It is possible to continue without "
@@ -258,32 +332,36 @@ msgid ""
 "Would you like to continue without migrating %s?"
 msgstr ""
 
-#: ../fsset.py:1046
+#: ../fsset.py:1413
+msgid "RAID Device"
+msgstr ""
+
+#: ../fsset.py:1416
 msgid "First sector of boot partition"
 msgstr ""
 
-#: ../fsset.py:1047
+#: ../fsset.py:1417
 msgid "Master Boot Record (MBR)"
 msgstr ""
 
-#: ../fsset.py:1113
+#: ../fsset.py:1537
 #, python-format
 msgid ""
 "An error occurred trying to initialize swap on device %s.  This problem is "
 "serious, and the install cannot continue.\n"
 "\n"
-"Press <Enter> to reboot your system."
+"Press <Enter> to exit the installer."
 msgstr ""
 
-#: ../fsset.py:1142
+#: ../fsset.py:1566
 msgid "Skip"
 msgstr ""
 
-#: ../fsset.py:1142 ../tui_complete.py:40
-msgid "Reboot"
+#: ../fsset.py:1566 ../fsset.py:1830 ../fsset.py:2581
+msgid "_Exit installer"
 msgstr ""
 
-#: ../fsset.py:1163
+#: ../fsset.py:1587
 #, python-format
 msgid ""
 "The swap device:\n"
@@ -295,11 +373,11 @@ msgid ""
 "installer will ignore it during the installation."
 msgstr ""
 
-#: ../fsset.py:1170
+#: ../fsset.py:1594
 msgid "Reformat"
 msgstr ""
 
-#: ../fsset.py:1174
+#: ../fsset.py:1598
 #, python-format
 msgid ""
 "The swap device:\n"
@@ -311,7 +389,7 @@ msgid ""
 "down your system rather than hibernating it."
 msgstr ""
 
-#: ../fsset.py:1182
+#: ../fsset.py:1606
 #, python-format
 msgid ""
 "The swap device:\n"
@@ -323,143 +401,137 @@ msgid ""
 "make sure the installer is set to format all swap partitions."
 msgstr ""
 
-#: ../fsset.py:1192
+#: ../fsset.py:1616
 msgid ""
 "\n"
 "\n"
 "Choose Skip if you want the installer to ignore this partition during the "
-"upgrade.  Choose Format to reformat the partition as swap space.  Choose "
-"Reboot to restart the system."
+"upgrade.  Choose Format to reformat the partition as swap space."
 msgstr ""
 
-#: ../fsset.py:1198
+#: ../fsset.py:1621
 msgid "Format"
 msgstr ""
 
-#: ../fsset.py:1206
+#: ../fsset.py:1627
 #, python-format
 msgid ""
 "Error enabling swap device %s: %s\n"
 "\n"
-"The /etc/fstab on your upgrade partition does not reference a valid swap "
-"partition.\n"
+"Devices in /etc/fstab should be specified by label, not by device name.\n"
 "\n"
-"Press OK to reboot your system."
+"Press OK to exit the installer."
 msgstr ""
 
-#: ../fsset.py:1217
+#: ../fsset.py:1632
 #, python-format
 msgid ""
 "Error enabling swap device %s: %s\n"
 "\n"
-"This most likely means this swap partition has not been initialized.\n"
+"The /etc/fstab on your upgrade partition does not reference a valid swap "
+"partition.\n"
 "\n"
-"Press OK to reboot your system."
+"Press OK to exit the installer"
 msgstr ""
 
-#: ../fsset.py:1271
+#: ../fsset.py:1638
 #, python-format
 msgid ""
-"Bad blocks have been detected on device /dev/%s. We do not recommend you use "
-"this device.\n"
+"Error enabling swap device %s: %s\n"
 "\n"
-"Press <Enter> to reboot your system"
-msgstr ""
-
-#: ../fsset.py:1282
-#, python-format
-msgid ""
-"An error occurred searching for bad blocks on %s.  This problem is serious, "
-"and the install cannot continue.\n"
+"This most likely means this swap partition has not been initialized.\n"
 "\n"
-"Press <Enter> to reboot your system."
+"Press OK to exit the installer."
 msgstr ""
 
-#: ../fsset.py:1304
+#: ../fsset.py:1712
 #, python-format
 msgid ""
 "An error occurred trying to format %s.  This problem is serious, and the "
 "install cannot continue.\n"
 "\n"
-"Press <Enter> to reboot your system."
+"Press <Enter> to exit the installer."
 msgstr ""
 
-#: ../fsset.py:1353
+#: ../fsset.py:1762
 #, python-format
 msgid ""
 "An error occurred trying to migrate %s.  This problem is serious, and the "
 "install cannot continue.\n"
 "\n"
-"Press <Enter> to reboot your system."
+"Press <Enter> to exit the installer."
 msgstr ""
 
-#: ../fsset.py:1380 ../fsset.py:1389
+#: ../fsset.py:1798 ../fsset.py:1807
 msgid "Invalid mount point"
 msgstr ""
 
-#: ../fsset.py:1381
+#: ../fsset.py:1799
 #, python-format
 msgid ""
 "An error occurred when trying to create %s.  Some element of this path is "
 "not a directory. This is a fatal error and the install cannot continue.\n"
 "\n"
-"Press <Enter> to reboot your system."
+"Press <Enter> to exit the installer."
 msgstr ""
 
-#: ../fsset.py:1390
+#: ../fsset.py:1808
 #, python-format
 msgid ""
 "An error occurred when trying to create %s: %s.  This is a fatal error and "
 "the install cannot continue.\n"
 "\n"
-"Press <Enter> to reboot your system."
+"Press <Enter> to exit the installer."
 msgstr ""
 
-#: ../fsset.py:1403
+#: ../fsset.py:1822
 msgid "Unable to mount filesystem"
 msgstr ""
 
-#: ../fsset.py:1404
+#: ../fsset.py:1823
 #, python-format
 msgid ""
 "An error occurred mounting device %s as %s.  You may continue installation, "
 "but there may be problems."
 msgstr ""
 
-#: ../fsset.py:1411 ../fsset.py:1820 ../fsset.py:1827 ../packages.py:131
-#: ../partedUtils.py:578 ../tui_confirm.py:37
-msgid "_Reboot"
-msgstr ""
-
-#: ../fsset.py:1411 ../partedUtils.py:578
+#: ../fsset.py:1831 ../partedUtils.py:982
 msgid "_Continue"
 msgstr ""
 
-#: ../fsset.py:1419
+#: ../fsset.py:1839
 #, python-format
 msgid ""
 "Error mounting device %s as %s: %s\n"
 "\n"
-"Devices in /etc/fstab should be specified by label, not by device name.\n"
+"Devices in /etc/fstab should be specified by label or UUID, not by device "
+"name.\n"
 "\n"
-"Press OK to reboot your system."
+"Press OK to exit the installer."
 msgstr ""
 
-#: ../fsset.py:1426
+#: ../fsset.py:1846
 #, python-format
 msgid ""
 "Error mounting device %s as %s: %s\n"
 "\n"
-"This most likely means this partition has not been formatted.\n"
+"Press OK to exit the installer."
+msgstr ""
+
+#: ../fsset.py:1864
+msgid ""
+"Error finding / entry.\n"
+"\n"
+"This is most likely means that your fstab is incorrect.\n"
 "\n"
-"Press OK to reboot your system."
+"Press OK to exit the installer."
 msgstr ""
 
-#: ../fsset.py:1812
+#: ../fsset.py:2573
 msgid "Duplicate Labels"
 msgstr ""
 
-#: ../fsset.py:1813
+#: ../fsset.py:2574
 #, python-format
 msgid ""
 "Multiple devices on your system are labelled %s.  Labels across devices must "
@@ -468,22 +540,11 @@ msgid ""
 "Please fix this problem and restart the installation process."
 msgstr ""
 
-#: ../fsset.py:1822
-msgid "Invalid Label"
-msgstr ""
-
-#: ../fsset.py:1823
-#, python-format
-msgid ""
-"An invalid label was found on device %s.  Please fix this problem and "
-"restart the installation process."
-msgstr ""
-
-#: ../fsset.py:2049
+#: ../fsset.py:2739
 msgid "Formatting"
 msgstr ""
 
-#: ../fsset.py:2050
+#: ../fsset.py:2740
 #, python-format
 msgid "Formatting %s file system..."
 msgstr ""
@@ -835,6 +896,10 @@ msgstr ""
 msgid "_Back"
 msgstr ""
 
+#: ../packages.py:131 ../partedUtils.py:981 ../tui_confirm.py:37
+msgid "_Reboot"
+msgstr ""
+
 #: ../packages.py:132
 msgid "Rebooting System"
 msgstr ""
@@ -899,11 +964,11 @@ msgstr ""
 msgid "Installation Progress"
 msgstr ""
 
-#: ../partedUtils.py:191 ../tui_partition.py:408
+#: ../partedUtils.py:239 ../tui_partition.py:408
 msgid "Foreign"
 msgstr ""
 
-#: ../partedUtils.py:231
+#: ../partedUtils.py:329
 #, python-format
 msgid ""
 "/dev/%s currently has a %s partition layout.  To use this drive for the "
@@ -913,19 +978,21 @@ msgid ""
 "Would you like to re-initialize this drive?"
 msgstr ""
 
-#: ../partedUtils.py:239
+#: ../partedUtils.py:338
 msgid "_Ignore drive"
 msgstr ""
 
-#: ../partedUtils.py:240
+#: ../partedUtils.py:339
 msgid "_Re-initialize drive"
 msgstr ""
 
-#: ../partedUtils.py:532
+#: ../partedUtils.py:842
 #, python-format
 msgid ""
-"The partition table on device %s was unreadable. To create new partitions it "
-"must be initialized, causing the loss of ALL DATA on this drive.\n"
+"The partition table on device %s (%s %-0.f MB) was unreadable.\n"
+"\n"
+"To create new partitions it must be initialized, causing the loss of ALL "
+"DATA on this drive.\n"
 "\n"
 "This operation will override any previous installation choices about which "
 "drives to ignore.\n"
@@ -933,7 +1000,7 @@ msgid ""
 "Would you like to initialize this drive, erasing ALL DATA?"
 msgstr ""
 
-#: ../partedUtils.py:569
+#: ../partedUtils.py:972
 #, python-format
 msgid ""
 "The drive /dev/%s has more than 15 partitions on it.  The SCSI subsystem in "
@@ -942,75 +1009,136 @@ msgid ""
 "any partitions beyond /dev/%s15 in %s"
 msgstr ""
 
-#: ../partedUtils.py:659
+#: ../partedUtils.py:1056
 msgid "No Drives Found"
 msgstr ""
 
-#: ../partedUtils.py:660
+#: ../partedUtils.py:1057
 msgid ""
 "An error has occurred - no valid devices were found on which to create new "
 "file systems. Please check your hardware for the cause of this problem."
 msgstr ""
 
+#: ../partIntfHelpers.py:43
+msgid "Please enter a volume group name."
+msgstr ""
+
+#: ../partIntfHelpers.py:47
+msgid "Volume Group Names must be less than 128 characters"
+msgstr ""
+
 #: ../partIntfHelpers.py:50
 #, python-format
+msgid "Error - the volume group name %s is not valid."
+msgstr ""
+
+#: ../partIntfHelpers.py:55
+msgid ""
+"Error - the volume group name contains illegal characters or spaces.  "
+"Acceptable characters are letters, digits, '.' or '_'."
+msgstr ""
+
+#: ../partIntfHelpers.py:65
+msgid "Please enter a logical volume name."
+msgstr ""
+
+#: ../partIntfHelpers.py:69
+msgid "Logical Volume Names must be less than 128 characters"
+msgstr ""
+
+#: ../partIntfHelpers.py:73
+#, python-format
+msgid "Error - the logical volume name %s is not valid."
+msgstr ""
+
+#: ../partIntfHelpers.py:79
+msgid ""
+"Error - the logical volume name contains illegal characters or spaces.  "
+"Acceptable characters are letters, digits, '.' or '_'."
+msgstr ""
+
+#: ../partIntfHelpers.py:103
+#, python-format
 msgid ""
 "The mount point %s is invalid.  Mount points must start with '/' and cannot "
 "end with '/', and must contain printable characters and no spaces."
 msgstr ""
 
-#: ../partIntfHelpers.py:57
+#: ../partIntfHelpers.py:110
 msgid "Please specify a mount point for this partition."
 msgstr ""
 
-#: ../partIntfHelpers.py:74 ../partIntfHelpers.py:80 ../partIntfHelpers.py:90
-#: ../partIntfHelpers.py:111
+#: ../partIntfHelpers.py:120
+#, python-format
+msgid "This partition is part of the RAID device /dev/md%s."
+msgstr ""
+
+#: ../partIntfHelpers.py:123
+msgid "This partition is part of a RAID device."
+msgstr ""
+
+#: ../partIntfHelpers.py:128
+#, python-format
+msgid "This partition is part of the LVM volume group '%s'."
+msgstr ""
+
+#: ../partIntfHelpers.py:131
+msgid "This partition is part of a LVM volume group."
+msgstr ""
+
+#: ../partIntfHelpers.py:146 ../partIntfHelpers.py:154
+#: ../partIntfHelpers.py:161 ../partIntfHelpers.py:171
+#: ../partIntfHelpers.py:195
 msgid "Unable To Delete"
 msgstr ""
 
-#: ../partIntfHelpers.py:75
+#: ../partIntfHelpers.py:147
 msgid "You must first select a partition to delete."
 msgstr ""
 
-#: ../partIntfHelpers.py:81
+#: ../partIntfHelpers.py:155
 msgid "You cannot delete free space."
 msgstr ""
 
-#: ../partIntfHelpers.py:91
+#: ../partIntfHelpers.py:162
+msgid "You cannot delete a partition of a LDL formatted DASD."
+msgstr ""
+
+#: ../partIntfHelpers.py:172
 #, python-format
 msgid ""
 "You cannot delete this partition, as it is an extended partition which "
 "contains %s"
 msgstr ""
 
-#: ../partIntfHelpers.py:106
+#: ../partIntfHelpers.py:190
 msgid "This partition is holding the data for the hard drive install."
 msgstr ""
 
-#: ../partIntfHelpers.py:112
+#: ../partIntfHelpers.py:196
 msgid ""
 "You cannot delete this partition:\n"
 "\n"
 msgstr ""
 
-#: ../partIntfHelpers.py:146 ../partIntfHelpers.py:391
+#: ../partIntfHelpers.py:241 ../partIntfHelpers.py:535
 msgid "Confirm Delete"
 msgstr ""
 
-#: ../partIntfHelpers.py:147
+#: ../partIntfHelpers.py:242
 #, python-format
 msgid "You are about to delete all partitions on the device '/dev/%s'."
 msgstr ""
 
-#: ../partIntfHelpers.py:150 ../partIntfHelpers.py:392
+#: ../partIntfHelpers.py:245 ../partIntfHelpers.py:536
 msgid "_Delete"
 msgstr ""
 
-#: ../partIntfHelpers.py:206
+#: ../partIntfHelpers.py:302
 msgid "Notice"
 msgstr ""
 
-#: ../partIntfHelpers.py:207
+#: ../partIntfHelpers.py:303
 #, python-format
 msgid ""
 "The following partitions were not deleted because they are in use:\n"
@@ -1018,33 +1146,33 @@ msgid ""
 "%s"
 msgstr ""
 
-#: ../partIntfHelpers.py:222 ../partIntfHelpers.py:238
-#: ../partIntfHelpers.py:249
+#: ../partIntfHelpers.py:319 ../partIntfHelpers.py:332
+#: ../partIntfHelpers.py:358 ../partIntfHelpers.py:369
 msgid "Unable To Edit"
 msgstr ""
 
-#: ../partIntfHelpers.py:223
+#: ../partIntfHelpers.py:320
 msgid "You must select a partition to edit"
 msgstr ""
 
-#: ../partIntfHelpers.py:239
-#, python-format
+#: ../partIntfHelpers.py:332 ../partIntfHelpers.py:370
 msgid ""
-"You cannot edit this partition, as it is an extended partition which "
-"contains %s"
+"You cannot edit this partition:\n"
+"\n"
 msgstr ""
 
-#: ../partIntfHelpers.py:250
+#: ../partIntfHelpers.py:359
+#, python-format
 msgid ""
-"You cannot edit this partition:\n"
-"\n"
+"You cannot edit this partition, as it is an extended partition which "
+"contains %s"
 msgstr ""
 
-#: ../partIntfHelpers.py:272
+#: ../partIntfHelpers.py:391
 msgid "Format as Swap?"
 msgstr ""
 
-#: ../partIntfHelpers.py:273
+#: ../partIntfHelpers.py:392
 #, python-format
 msgid ""
 "/dev/%s has a partition type of 0x82 (Linux swap) but does not appear to be "
@@ -1053,38 +1181,38 @@ msgid ""
 "Would you like to format this partition as a swap partition?"
 msgstr ""
 
-#: ../partIntfHelpers.py:288
+#: ../partIntfHelpers.py:412
 #, python-format
 msgid "You need to select at least one hard drive to install %s."
 msgstr ""
 
-#: ../partIntfHelpers.py:293
+#: ../partIntfHelpers.py:417
 msgid ""
 "You have chosen to use a pre-existing partition for this installation "
 "without formatting it. We recommend that you format this partition to make "
 "sure files from a previous operating system installation do not cause "
 "problems with this installation of Linux. However, if this partition "
-"contains files that you need to keep, such as home directories, then  "
+"contains files that you need to keep, such as home directories, then "
 "continue without formatting this partition."
 msgstr ""
 
-#: ../partIntfHelpers.py:301
+#: ../partIntfHelpers.py:425
 msgid "Format?"
 msgstr ""
 
-#: ../partIntfHelpers.py:302
+#: ../partIntfHelpers.py:425
 msgid "_Modify Partition"
 msgstr ""
 
-#: ../partIntfHelpers.py:302
+#: ../partIntfHelpers.py:425
 msgid "Do _Not Format"
 msgstr ""
 
-#: ../partIntfHelpers.py:311
+#: ../partIntfHelpers.py:433
 msgid "Error with Partitioning"
 msgstr ""
 
-#: ../partIntfHelpers.py:312
+#: ../partIntfHelpers.py:434
 #, python-format
 msgid ""
 "The following critical errors exist with your requested partitioning scheme. "
@@ -1093,11 +1221,11 @@ msgid ""
 "%s"
 msgstr ""
 
-#: ../partIntfHelpers.py:326
+#: ../partIntfHelpers.py:448
 msgid "Partitioning Warning"
 msgstr ""
 
-#: ../partIntfHelpers.py:327
+#: ../partIntfHelpers.py:449
 #, python-format
 msgid ""
 "The following warnings exist with your requested partition scheme.\n"
@@ -1107,45 +1235,62 @@ msgid ""
 "Would you like to continue with your requested partitioning scheme?"
 msgstr ""
 
-#: ../partIntfHelpers.py:340
+#: ../partIntfHelpers.py:463
 msgid ""
 "The following pre-existing partitions have been selected to be formatted, "
 "destroying all data."
 msgstr ""
 
-#: ../partIntfHelpers.py:342
+#: ../partIntfHelpers.py:466
 msgid ""
 "Select 'Yes' to continue and format these partitions, or 'No' to go back and "
 "change these settings."
 msgstr ""
 
-#: ../partIntfHelpers.py:348
+#: ../partIntfHelpers.py:472
 msgid "Format Warning"
 msgstr ""
 
-#: ../partIntfHelpers.py:387
+#: ../partIntfHelpers.py:520
+#, python-format
+msgid ""
+"You are about to delete the volume group \"%s\".\n"
+"\n"
+"ALL logical volumes in this volume group will be lost!"
+msgstr ""
+
+#: ../partIntfHelpers.py:524
+#, python-format
+msgid "You are about to delete the logical volume \"%s\"."
+msgstr ""
+
+#: ../partIntfHelpers.py:527
+msgid "You are about to delete a RAID device."
+msgstr ""
+
+#: ../partIntfHelpers.py:530
 #, python-format
 msgid "You are about to delete the /dev/%s partition."
 msgstr ""
 
-#: ../partIntfHelpers.py:389
+#: ../partIntfHelpers.py:533
 msgid "The partition you selected will be deleted."
 msgstr ""
 
-#: ../partIntfHelpers.py:398
+#: ../partIntfHelpers.py:543
 msgid "Confirm Reset"
 msgstr ""
 
-#: ../partIntfHelpers.py:399
+#: ../partIntfHelpers.py:544
 msgid ""
 "Are you sure you want to reset the partition table to its original state?"
 msgstr ""
 
-#: ../partitioning.py:36
+#: ../partitioning.py:36 ../partitions.py:81
 msgid "Installation cannot continue."
 msgstr ""
 
-#: ../partitioning.py:37
+#: ../partitioning.py:37 ../partitions.py:82
 msgid ""
 "The partitioning options you have chosen have already been activated. You "
 "can no longer return to the disk editing screen. Would you like to continue "
@@ -1163,119 +1308,215 @@ msgid ""
 "disk immediately. Is that OK?"
 msgstr ""
 
-#: ../partitions.py:311
+#: ../partitions.py:122
+msgid "Encrypt device?"
+msgstr ""
+
+#: ../partitions.py:123
+msgid ""
+"You specified block device encryption should be enabled, but you have not "
+"supplied a passphrase. If you do not go back and provide a passphrase, block "
+"device encryption will be disabled."
+msgstr ""
+
+#: ../partitions.py:130 ../partitions.py:260
+msgid "Continue"
+msgstr ""
+
+#: ../partitions.py:151
+msgid "Writing partitioning to disk"
+msgstr ""
+
+#: ../partitions.py:152
+msgid ""
+"The partitioning options you have selected will now be written to disk.  Any "
+"data on deleted or reformatted partitions will be lost."
+msgstr ""
+
+#: ../partitions.py:157
+msgid "Go _back"
+msgstr ""
+
+#: ../partitions.py:158
+msgid "_Write changes to disk"
+msgstr ""
+
+#: ../partitions.py:265
+msgid "Confirm"
+msgstr ""
+
+#: ../partitions.py:266
+#, python-format
+msgid ""
+"Are you sure you want to skip entering a passphrase for device %s?\n"
+"\n"
+"If you skip this step the device's contents will not be available during "
+"installation."
+msgstr ""
+
+#: ../partitions.py:1077
 #, python-format
 msgid ""
 "You have not defined a root partition (/), which is required for "
 "installation of %s to continue."
 msgstr ""
 
-#: ../partitions.py:316
+#: ../partitions.py:1082
 #, python-format
 msgid ""
 "Your root partition is less than 250 megabytes which is usually too small to "
 "install %s."
 msgstr ""
 
-#: ../partitions.py:333
-msgid ""
-"Your boot partition isn't on one of the first four partitions and thus won't "
-"be bootable."
-msgstr ""
-
-#: ../partitions.py:342
+#: ../partitions.py:1108
 #, python-format
 msgid ""
 "Your %s partition is less than %s megabytes which is lower than recommended "
 "for a normal %s install."
 msgstr ""
 
-#: ../partitions.py:374
+#: ../partitions.py:1142
 msgid ""
 "Installing on a USB device.  This may or may not produce a working system."
 msgstr ""
 
-#: ../partitions.py:378
+#: ../partitions.py:1145
 msgid ""
 "Installing on a FireWire device.  This may or may not produce a working "
 "system."
 msgstr ""
 
-#: ../partitions.py:391
+#: ../partitions.py:1155
+msgid "Bootable partitions can only be on RAID1 devices."
+msgstr ""
+
+#: ../partitions.py:1160
+msgid "Bootable partitions cannot be on a logical volume."
+msgstr ""
+
+#: ../partitions.py:1166
+msgid "Bootable partitions cannot be on a RAID device."
+msgstr ""
+
+#: ../partitions.py:1171 ../partitions.py:1177
+#, python-format
+msgid "Bootable partitions cannot be on an %s filesystem."
+msgstr ""
+
+#: ../partitions.py:1181
+msgid "Bootable partitions cannot be on an encrypted block device"
+msgstr ""
+
+#: ../partitions.py:1185
 msgid ""
 "You have not specified a swap partition.  Although not strictly required in "
 "all cases, it will significantly improve performance for most installations."
 msgstr ""
 
-#: ../partitions.py:398
+#: ../partitions.py:1192
 #, python-format
 msgid ""
 "You have specified more than 32 swap devices.  The kernel for %s only "
 "supports 32 swap devices."
 msgstr ""
 
-#: ../partitions.py:409
+#: ../partitions.py:1203
 #, python-format
 msgid ""
 "You have allocated less swap space (%dM) than available RAM (%dM) on your "
 "system.  This could negatively impact performance."
 msgstr ""
 
-#: ../partitions.py:485
+#: ../partitions.py:1508
 msgid "the partition in use by the installer."
 msgstr ""
 
-#: ../partRequests.py:182
+#: ../partitions.py:1511
+msgid "a partition which is a member of a RAID array."
+msgstr ""
+
+#: ../partitions.py:1514
+msgid "a partition which is a member of a LVM Volume Group."
+msgstr ""
+
+#: ../partRequests.py:275 ../partRequests.py:278
+#, python-format
+msgid "The mount point %s must be formatted during live CD installs."
+msgstr ""
+
+#: ../partRequests.py:284
 #, python-format
 msgid ""
-"This mount point is invalid. The %s directory must be on the / file system."
+"This mount point is invalid.  The %s directory must be on the / file system."
 msgstr ""
 
-#: ../partRequests.py:185
+#: ../partRequests.py:287
 #, python-format
 msgid ""
 "The mount point %s cannot be used.  It must be a symbolic link for proper "
 "system operation.  Please select a different mount point."
 msgstr ""
 
-#: ../partRequests.py:192
+#: ../partRequests.py:296
 msgid "This mount point must be on a linux file system."
 msgstr ""
 
-#: ../partRequests.py:212
+#: ../partRequests.py:317
 #, python-format
 msgid ""
 "The mount point \"%s\" is already in use, please choose a different mount "
 "point."
 msgstr ""
 
-#: ../partRequests.py:226
+#: ../partRequests.py:331
 #, python-format
 msgid ""
 "The size of the %s partition (%10.2f MB) exceeds the maximum size of %10.2f "
 "MB."
 msgstr ""
 
-#: ../partRequests.py:414
+#: ../partRequests.py:539
 #, python-format
 msgid ""
 "The size of the requested partition (size = %s MB) exceeds the maximum size "
 "of %s MB."
 msgstr ""
 
-#: ../partRequests.py:419
+#: ../partRequests.py:544
 #, python-format
 msgid "The size of the requested partition is negative! (size = %s MB)"
 msgstr ""
 
-#: ../partRequests.py:423
+#: ../partRequests.py:548
 msgid "Partitions can't start below the first cylinder."
 msgstr ""
 
-#: ../partRequests.py:426
+#: ../partRequests.py:551
 msgid "Partitions can't end on a negative cylinder."
 msgstr ""
 
+#: ../partRequests.py:753
+msgid "No members in RAID request, or not RAID level specified."
+msgstr ""
+
+#: ../partRequests.py:758
+#, python-format
+msgid "A RAID device of type %s requires at least %s members."
+msgstr ""
+
+#: ../partRequests.py:767
+#, python-format
+msgid ""
+"This RAID device can have a maximum of %s spares. To have more spares you "
+"will need to add members to the RAID device."
+msgstr ""
+
+#: ../partRequests.py:1033
+msgid ""
+"Logical volume size must be larger than the volume group's physical extent "
+"size."
+msgstr ""
+
 #: ../tui_bootloader.py:27
 msgid "Which boot loader would you like to use?"
 msgstr ""
@@ -1458,6 +1699,10 @@ msgstr ""
 msgid "Complete"
 msgstr ""
 
+#: ../tui_complete.py:40
+msgid "Reboot"
+msgstr ""
+
 #: ../tui_confirm.py:23
 msgid "Installation to begin"
 msgstr ""
@@ -1757,11 +2002,11 @@ msgstr ""
 msgid "<Space>,<+>,<-> selection   |   <F2> Add drive   |   <F12> next screen"
 msgstr ""
 
-#: ../tui_partition.py:1029
+#: ../tui_partition.py:1026
 msgid "Review Partition Layout"
 msgstr ""
 
-#: ../tui_partition.py:1030
+#: ../tui_partition.py:1027
 msgid "Review and modify partitioning layout?"
 msgstr ""
 
index 0d063751eba9d3fe880d593cab310da699aaf2a9..1e06e511e3e8e58028baefbff7a5bdc12bf41108 100644 (file)
@@ -10,4 +10,5 @@ setattr
 ^.*isys/isys.py:[0-9]*: Object \(.*_iface\) has no attribute \(Get.*\)$
 ^partedUtils.py:[0-9]*: No module attribute \(__dict__\) found$
 ^partedUtils.py:[0-9]*: No module attribute \(DEVICE_SX8\) found$
+^partRequests.py:[0-9]*: Format string argument count \(0\) doesn't match arguments \(1\)$
 Note this last line must never end with a newline
\ No newline at end of file
diff --git a/src/pomona/raid.py b/src/pomona/raid.py
new file mode 100644 (file)
index 0000000..312f71b
--- /dev/null
@@ -0,0 +1,226 @@
+#
+# raid.py - raid probing control
+#
+# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
+# Red Hat, Inc.  All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+# Author(s): Erik Troan <ewt@redhat.com>
+#
+
+"""Raid probing control."""
+
+def getRaidLevels():
+    avail = []
+    try:
+        f = open("/proc/mdstat", "r")
+    except:
+        pass
+    else:
+        for l in f.readlines():
+            if not l.startswith("Personalities"):
+                continue
+
+            lst = l.split()
+
+            for lev in ["RAID0", "RAID1", "RAID5", "RAID6", "RAID10"]:
+                if "[" + lev + "]" in lst or "[" + lev.lower() + "]" in lst:
+                    avail.append(lev)
+
+        f.close()
+
+    avail.sort()
+    return avail
+
+# XXX define availraidlevels and defaultmntpts as arch characteristics
+availRaidLevels = getRaidLevels()
+
+import parted
+import isys
+import os
+import partitions
+import partedUtils
+
+import logging
+log = logging.getLogger("pomona")
+
+# these arches can have their /boot on RAID and not have their
+# boot loader blow up
+raidBootArches = [ "i386", "x86_64", "ppc" ]
+
+def scanForRaid(drives):
+    """Scans for raid devices on drives.
+
+    drives is a list of device names.
+    Returns a list of (mdMinor, devices, level, totalDisks) tuples.
+    """
+
+    raidSets = {}
+    raidDevices = {}
+
+    for d in drives:
+        parts = []
+        try:
+            dev = parted.PedDevice.get("/dev/%s" % (d,))
+            disk = parted.PedDisk.new(dev)
+
+            raidParts = partedUtils.get_raid_partitions(disk)
+            for part in raidParts:
+                # if the part is encrypted, add the mapped dev instead
+                pname = partedUtils.get_partition_name(part)
+                cryptoDev = partitions.Partitions.encryptedDevices.get(pname)
+                if cryptoDev and not cryptoDev.openDevice():
+                    dev = cryptoDev.getDevice()
+                else:
+                    dev = pname
+                parts.append(dev)
+        except:
+            pass
+
+        for dev in parts:
+            try:
+                (major, minor, raidSet, level, nrDisks, totalDisks, mdMinor) =\
+                        isys.raidsb(dev)
+            except ValueError:
+                # bad magic, this can't be part of our raid set
+                log.error("reading raid sb failed for %s",dev)
+                continue
+
+            if raidSets.has_key(raidSet):
+                (knownLevel, knownDisks, knownMinor, knownDevices) = \
+                        raidSets[raidSet]
+                if knownLevel != level or knownDisks != totalDisks or \
+                   knownMinor != mdMinor:
+                    # Raise hell
+                    log.error("raid set inconsistency for md%d: "
+                              "all drives in this raid set do not "
+                              "agree on raid parameters.  Skipping raid device",
+                              mdMinor)
+                    continue
+                knownDevices.append(dev)
+                raidSets[raidSet] = (knownLevel, knownDisks, knownMinor,
+                                     knownDevices)
+            else:
+                raidSets[raidSet] = (level, totalDisks, mdMinor, [dev,])
+
+            if raidDevices.has_key(mdMinor):
+                if (raidDevices[mdMinor] != raidSet):
+                    log.error("raid set inconsistency for md%d: "
+                              "found members of multiple raid sets "
+                              "that claim to be md%d.  Using only the first "
+                              "array found.", mdMinor, mdMinor)
+                    continue
+            else:
+                raidDevices[mdMinor] = raidSet
+
+    raidList = []
+    for key in raidSets.keys():
+        (level, totalDisks, mdMinor, devices) = raidSets[key]
+        if len(devices) < totalDisks:
+            log.warning("missing components of raid device md%d.  The "
+                        "raid device needs %d drive(s) and only %d (was/were) "
+                        "found. This raid device will not be started.", mdMinor,
+                        totalDisks, len(devices))
+            continue
+        raidList.append((mdMinor, devices, level, totalDisks))
+
+    return raidList
+
+def startAllRaid(driveList):
+    """Do a raid start on raid devices and return a list like scanForRaid."""
+    rc = []
+    mdList = scanForRaid(driveList)
+    for mdDevice, deviceList, level, numActive in mdList:
+        devName = "md%d" % (mdDevice,)
+        isys.raidstart(devName, deviceList[0])
+        rc.append((devName, deviceList, level, numActive))
+    return rc
+
+def stopAllRaid(mdList):
+    """Do a raid stop on each of the raid device tuples given."""
+    for dev, devices, level, numActive in mdList:
+        isys.raidstop(dev)
+
+def isRaid10(raidlevel):
+    """Return whether raidlevel is a valid descriptor of RAID10."""
+    if raidlevel in ("RAID10", "10", 10):
+        return True
+    return False
+
+def isRaid6(raidlevel):
+    """Return whether raidlevel is a valid descriptor of RAID6."""
+    if raidlevel in ("RAID6", "6", 6):
+        return True
+    return False
+
+def isRaid5(raidlevel):
+    """Return whether raidlevel is a valid descriptor of RAID5."""
+    if raidlevel in ("RAID5", "5", 5):
+        return True
+    return False
+
+def isRaid1(raidlevel):
+    """Return whether raidlevel is a valid descriptor of RAID1."""
+    if raidlevel in ("mirror", "RAID1", "1", 1):
+        return True
+    return False
+
+def isRaid0(raidlevel):
+    """Return whether raidlevel is a valid descriptor of RAID0."""
+    if raidlevel in ("stripe", "RAID0", "0", 0):
+        return True
+    return False
+
+def get_raid_min_members(raidlevel):
+    """Return the minimum number of raid members required for raid level"""
+    if isRaid0(raidlevel):
+        return 2
+    elif isRaid1(raidlevel):
+        return 2
+    elif isRaid5(raidlevel):
+        return 3
+    elif isRaid6(raidlevel):
+        return 4
+    elif isRaid10(raidlevel):
+        return 4
+    else:
+        raise ValueError, "invalid raidlevel in get_raid_min_members"
+
+def get_raid_max_spares(raidlevel, nummembers):
+    """Return the maximum number of raid spares for raidlevel."""
+    if isRaid0(raidlevel):
+        return 0
+    elif isRaid1(raidlevel) or isRaid5(raidlevel) or isRaid6(raidlevel) or isRaid10(raidlevel):
+        return max(0, nummembers - get_raid_min_members(raidlevel))
+    else:
+        raise ValueError, "invalid raidlevel in get_raid_max_spares"
+
+def register_raid_device(mdname, newdevices, newlevel, newnumActive):
+    """Register a new RAID device in the mdlist."""
+    for dev, devices, level, numActive in partedUtils.DiskSet.mdList:
+        if mdname == dev:
+            if (devices != newdevices or level != newlevel or numActive != newnumActive):
+                raise ValueError, "%s is already in the mdList!" % (mdname,)
+            else:
+                return
+    partedUtils.DiskSet.mdList.append((mdname, newdevices[:], newlevel,
+                                       newnumActive))
+
+def lookup_raid_device(mdname):
+    """Return the requested RAID device information."""
+    for dev, devices, level, numActive in partedUtils.DiskSet.mdList:
+        if mdname == dev:
+            return (dev, devices, level, numActive)
+    raise KeyError, "md device not found"
index f9019ce6d7b3cde45f42dccd4f4943b5d5f8f738..b362a4dcb1826b92b7ca53568c552f6e2f510739 100644 (file)
@@ -5,7 +5,7 @@ import isys
 import parted
 import signal
 import time
-import inutil
+import iutil
 import imputil
 from constants import *
 
@@ -407,7 +407,7 @@ def spawnShell(screen):
     screen.suspend()
     print "\n\nType <exit> to return to the install program.\n"
     if os.path.exists("/bin/sh"):
-        inutil.execConsole()
+        iutil.execConsole()
     else:
         print "Unable to find /bin/sh to execute!  Not starting shell"
     time.sleep(5)
index d41d09c55c534e7a8becc90d98a9d2ae04c77860..e1c770eb5949b9b57acec475cc81a03eae8fefd9 100644 (file)
@@ -15,7 +15,7 @@ from snack import *
 from constants import *
 from flags import flags
 import string
-import inutil
+import iutil
 import bootloader
 
 import gettext
index ddd05df27616404c0709b16b256b9afbc354054b..0306c84cac8fcebe53052bcecd312bd2bf1400c3 100644 (file)
@@ -11,7 +11,7 @@
 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 #
 
-import inutil
+import iutil
 from snack import *
 from constants import *
 from flags import flags
index 66c986502e97129d0e7c1de43dd87cd3bc5382b2..041a8379ad03b7a3a4c57ea46be5837e856103aa 100644 (file)
@@ -661,7 +661,7 @@ class PartitionWindow:
                 filesystem = fstype.current()
 
                 if primary.selected():
-                    primonly = TRUE
+                    primonly = 1
                 else:
                     primonly = None
 
@@ -671,7 +671,7 @@ class PartitionWindow:
                     request.mountpoint = self.mount.value()
                 else:
                     request.mountpoint = None
-                request.format = TRUE
+                request.format = 1
                 request.primary = primonly
 
                 if badblocksCB is not None:
@@ -690,7 +690,7 @@ class PartitionWindow:
                     if growtype == "fixed":
                         grow = None
                     else:
-                        grow = TRUE
+                        grow = 1
                     if growtype == "limit":
                         if invalidInteger(limitentry.value()):
                             self.intf.messageWindow(_("Invalid Entry for Maximum Size"),
@@ -1018,9 +1018,6 @@ class PartitionTypeWindow:
                 pomona.id.partitions.autoClearPartType = partmethod_ans
                 pomona.id.partitions.autoClearPartDrives = sel
 
-                if queryAutoPartitionOK(pomona):
-                    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)
index 54c4a231794400b8bdd672129c2d98ff8623ddd1..c26253ff5111c6899fd9e3f5ec90ee2b2647ff9f 100644 (file)
@@ -13,7 +13,7 @@
 
 import sys
 import string
-import inutil
+import iutil
 import os
 from time import *
 from snack import *
@@ -36,7 +36,7 @@ class TimezoneWindow:
         if self.c.selected():
             args.append("--utc")
 
-        inutil.execWithRedirect("hwclock", args, searchPath=1)
+        iutil.execWithRedirect("hwclock", args, searchPath=1)
         self.g.setTimer(500)
         self.updateClock()